Improved syntax checking

pull/17/head
Michael Iacona 2018-11-05 20:16:54 -05:00
parent a6d410a38c
commit 4b5e893580
16 changed files with 188 additions and 185 deletions

View File

@ -28,6 +28,7 @@
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module",
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
@ -42,6 +43,7 @@
"_": true,
"async": true
// …and any others (e.g. `"Organization": true`)
//["error", "unix"]
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
},
@ -63,7 +65,7 @@
"ObjectExpression": 1,
"ignoredNodes": ["ConditionalExpression"]
}],
"linebreak-style": ["error", "unix"],
"linebreak-style": 0,
"no-dupe-keys": ["error"],
"no-duplicate-case": ["error"],
"no-extra-semi": ["warn"],

View File

@ -1,6 +1,6 @@
'use strict'
'use strict';
import Ajax from '../lib/Ajax'
import Ajax from '../lib/Ajax';
const ACTIONS = {
set_working: 'set_working',
@ -9,50 +9,50 @@ const ACTIONS = {
set_carousel: 'set_carousel',
set_error: 'set_error',
clear_error: 'clear_error'
}
};
export default ACTIONS
export default ACTIONS;
export const setWorking = working => ({
type: ACTIONS.set_working,
data: working
})
});
export const setEmail = email => ({
type: ACTIONS.set_user,
data: email
})
});
export const setPassword = pass => ({
type: ACTIONS.set_password,
data: pass
})
});
export const setCarousel = pos => (dispatch, getState) => {
dispatch(clearError())
dispatch(clearError());
dispatch({
type: ACTIONS.set_carousel,
data: pos
})
}
});
};
export const setError = data => ({
type: ACTIONS.set_error,
data: data
})
});
export const clearError = () => ({
type: ACTIONS.clear_error
})
});
export const setLoggedIn = (data) => (dispatch, getState) => {
window.localStorage.setItem('roe-token', JSON.stringify(data))
window.location.href = '/app'
}
window.localStorage.setItem('roe-token', JSON.stringify(data));
window.location.href = '/app';
};
export const checkEmail = email => async (dispatch, getState) => {
dispatch(setWorking(true))
dispatch(clearError())
dispatch(setWorking(true));
dispatch(clearError());
if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) {
try {
const res = await Ajax.post({
@ -60,25 +60,25 @@ export const checkEmail = email => async (dispatch, getState) => {
data: {
email
}
})
dispatch(setCarousel(2))
});
dispatch(setCarousel(2));
} catch (e) {
dispatch(setError({
type: 'email',
error: 'An account with that email does not exist.'
}))
}));
}
} else {
dispatch(setError({
type: 'email',
error: 'Please enter a valid email address.'
}))
}));
}
dispatch(setWorking(false))
}
dispatch(setWorking(false));
};
export const checkPassword = (email, password) => async (dispatch, getState) => {
dispatch(setWorking(true))
dispatch(setWorking(true));
// do email + password check
try {
@ -88,21 +88,21 @@ export const checkPassword = (email, password) => async (dispatch, getState) =>
identifier: email,
password
}
})
dispatch(setLoggedIn(res))
});
dispatch(setLoggedIn(res));
// dispatch(setWorking(false))
} catch (e) {
dispatch(setError({
type: 'password',
error: e.toString()
}))
dispatch(setWorking(false))
}));
dispatch(setWorking(false));
}
}
};
export const signup = (email, password) => async (dispatch, getState) => {
dispatch(setWorking(true))
dispatch(clearError())
dispatch(setWorking(true));
dispatch(clearError());
if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) {
try {
await Ajax.post({
@ -110,26 +110,26 @@ export const signup = (email, password) => async (dispatch, getState) => {
data: {
email
}
})
});
await Ajax.post({
url: '/register',
data: {
email,
password
}
})
dispatch(setCarousel(2))
});
dispatch(setCarousel(2));
} catch (e) {
dispatch(setError({
type: 'email',
error: e.toString()
}))
}));
}
} else {
dispatch(setError({
type: 'email',
error: 'Please enter a valid email address.'
}))
}));
}
dispatch(setWorking(false))
}
dispatch(setWorking(false));
};

View File

@ -1,162 +1,162 @@
/* global XMLHttpRequest FormData */
'use strict'
'use strict';
let ajaxcfg = {}
let ajaxcfg = {};
class AjaxError extends Error {
constructor (reason, data, xhr) {
super(reason)
this.data = data
this.xhr = xhr
super(reason);
this.data = data;
this.xhr = xhr;
}
}
export default class Ajax {
static async get (opts) {
if (!opts) opts = {}
opts.method = 'get'
return Ajax.ajax(opts)
if (!opts) {opts = {};}
opts.method = 'get';
return Ajax.ajax(opts);
}
static async post (opts) {
if (!opts) opts = {}
opts.method = 'post'
return Ajax.ajax(opts)
if (!opts) {opts = {};}
opts.method = 'post';
return Ajax.ajax(opts);
}
static async put (opts) {
if (!opts) opts = {}
opts.method = 'put'
return Ajax.ajax(opts)
if (!opts) {opts = {};}
opts.method = 'put';
return Ajax.ajax(opts);
}
static async patch (opts) {
if (!opts) opts = {}
opts.method = 'patch'
return Ajax.ajax(opts)
if (!opts) {opts = {};}
opts.method = 'patch';
return Ajax.ajax(opts);
}
static async delete (opts) {
if (!opts) opts = {}
opts.method = 'delete'
return Ajax.ajax(opts)
if (!opts) {opts = {};}
opts.method = 'delete';
return Ajax.ajax(opts);
}
static async head (opts) {
if (!opts) opts = {}
opts.method = 'head'
return Ajax.ajax(opts)
if (!opts) {opts = {};}
opts.method = 'head';
return Ajax.ajax(opts);
}
static async options (opts) {
if (!opts) opts = {}
opts.method = 'options'
return Ajax.ajax(opts)
if (!opts) {opts = {};}
opts.method = 'options';
return Ajax.ajax(opts);
}
static ajax (opts) {
return new Promise((resolve, reject) => {
if (!opts) reject(new Error('Missing required options parameter.'))
if (!opts) {reject(new Error('Missing required options parameter.'));}
if (opts.method) {
if (!['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(opts.method.toLowerCase())) reject(new Error('opts.method must be one of: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.'))
opts.method = opts.method.toUpperCase()
if (!['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(opts.method.toLowerCase())) {reject(new Error('opts.method must be one of: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.'));}
opts.method = opts.method.toUpperCase();
}
var xhr = opts.xhr || new XMLHttpRequest()
var xhr = opts.xhr || new XMLHttpRequest();
var fd = null
var qs = ''
var fd = null;
var qs = '';
if (opts.data && opts.method.toLowerCase() !== 'get') {
fd = new FormData()
fd = new FormData();
for (let key in opts.data) {
fd.append(key, opts.data[key])
fd.append(key, opts.data[key]);
}
} else if (opts.data) {
qs += '?'
let params = []
qs += '?';
let params = [];
for (let key in opts.data) {
params.push([key, opts.data[key]].join('='))
params.push([key, opts.data[key]].join('='));
}
qs += params.join('&')
qs += params.join('&');
}
xhr.onload = () => {
if (xhr.status !== 200) return xhr.onerror()
var data = xhr.response
if (xhr.status !== 200) {return xhr.onerror();}
var data = xhr.response;
resolve({
data,
xhr
})
}
});
};
xhr.onerror = () => {
var data = xhr.response
var data = xhr.response;
// method not allowed
if (xhr.status === 405) {
reject(new AjaxError('405 Method Not Allowed', data, xhr))
return
reject(new AjaxError('405 Method Not Allowed', data, xhr));
return;
} else if (xhr.status === 404) {
reject(new AjaxError('404 Not Found', data, xhr))
return
reject(new AjaxError('404 Not Found', data, xhr));
return;
}
try {
// if the access token is invalid, try to use the refresh token
var json = JSON.parse(data)
var json = JSON.parse(data);
if (json.error === 'access_denied' && json.hint.includes('token') && json.hint.includes('invalid') && ajaxcfg.refresh_token) {
return Ajax.refresh(opts)
return Ajax.refresh(opts);
} else if (json.error === 'access_denied' && json.hint.includes('token') && json.hint.includes('revoked')) {
reject(new AjaxError('token revoked', data, xhr))
reject(new AjaxError('token revoked', data, xhr));
}
} catch (e) {
reject(new AjaxError(e.toString(), data, xhr))
reject(new AjaxError(e.toString(), data, xhr));
} finally {
reject(new AjaxError(data, xhr.status, xhr))
reject(new AjaxError(data, xhr.status, xhr));
}
}
};
xhr.open(opts.method || 'GET', opts.url + qs || window.location.href)
xhr.open(opts.method || 'GET', opts.url + qs || window.location.href);
if (opts.headers) {
for (let key in opts.headers) xhr.setRequestHeader(key, opts.headers[key])
for (let key in opts.headers) {xhr.setRequestHeader(key, opts.headers[key]);}
}
if (ajaxcfg.access_token && !(opts.headers || {}).Authorization) xhr.setRequestHeader('Authorization', 'Bearer ' + ajaxcfg.access_token)
xhr.send(fd)
})
if (ajaxcfg.access_token && !(opts.headers || {}).Authorization) {xhr.setRequestHeader('Authorization', 'Bearer ' + ajaxcfg.access_token);}
xhr.send(fd);
});
}
static refresh (opts) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
var xhr = new XMLHttpRequest();
var fd = new FormData()
var fd = new FormData();
const OAUTH_TOKEN_REQUEST = {
grant_type: 'refresh_token',
refresh_token: ajaxcfg.refresh_token,
client_id: 'foxfile',
client_secret: 1
}
};
for (var key in OAUTH_TOKEN_REQUEST) {
fd.append(key, OAUTH_TOKEN_REQUEST[key])
fd.append(key, OAUTH_TOKEN_REQUEST[key]);
}
// try original request
xhr.onload = () => {
if (xhr.status !== 200) return xhr.onerror()
if (ajaxcfg.refresh) ajaxcfg.refresh(xhr.response)
var json = JSON.parse(xhr.response)
ajaxcfg.access_token = json.access_token
ajaxcfg.refresh_token = json.refresh_token
return Ajax.ajax(opts)
}
if (xhr.status !== 200) {return xhr.onerror();}
if (ajaxcfg.refresh) {ajaxcfg.refresh(xhr.response);}
var json = JSON.parse(xhr.response);
ajaxcfg.access_token = json.access_token;
ajaxcfg.refresh_token = json.refresh_token;
return Ajax.ajax(opts);
};
// if this fails, dont try again
xhr.onerror = () => {
var data = xhr.response
reject(new AjaxError(xhr.status, data, xhr))
}
xhr.open('POST', ajaxcfg.refresh_url)
xhr.send(fd)
})
var data = xhr.response;
reject(new AjaxError(xhr.status, data, xhr));
};
xhr.open('POST', ajaxcfg.refresh_url);
xhr.send(fd);
});
}
static setTokenData (tokens) {
if (!tokens) throw new Error('Missing tokens.')
if (!tokens.access_token && !tokens.refresh_token && !tokens.refresh_url) throw new Error('Missing at least one of: access_token, refresh_token, refresh_url.')
if (tokens.access_token) ajaxcfg.access_token = tokens.access_token
if (tokens.refresh_token) ajaxcfg.refresh_token = tokens.refresh_token
if (tokens.refresh_url) ajaxcfg.refresh_url = tokens.refresh_url
return true
if (!tokens) {throw new Error('Missing tokens.');}
if (!tokens.access_token && !tokens.refresh_token && !tokens.refresh_url) {throw new Error('Missing at least one of: access_token, refresh_token, refresh_url.');}
if (tokens.access_token) {ajaxcfg.access_token = tokens.access_token;}
if (tokens.refresh_token) {ajaxcfg.refresh_token = tokens.refresh_token;}
if (tokens.refresh_url) {ajaxcfg.refresh_url = tokens.refresh_url;}
return true;
}
static onRefresh (func) {
ajaxcfg.refresh = func
ajaxcfg.refresh = func;
}
}

View File

@ -1,9 +1,9 @@
'use strict'
'use strict';
import Actions from '../actions/login'
import Actions from '../actions/login';
const reducer = (state = {}, action) => {
const {type, data} = action
const {type, data} = action;
switch (type) {
case Actions.set_user:
return {
@ -11,40 +11,40 @@ const reducer = (state = {}, action) => {
...state.user,
email: data
}
}
};
case Actions.set_password:
return {
user: {
...state.user,
password: data
}
}
};
case Actions.set_carousel:
return {
carouselPosition: data
}
};
case Actions.set_working:
return {
working: data
}
};
case Actions.set_error:
switch (data.type) {
case 'email':
return {
emailError: data.error
}
};
case 'password':
return {
passwordError: data.error
}
default: return {}
};
default: return {};
}
case Actions.clear_error:
return {
emailError: '',
passwordError: ''
}
};
}
}
};
export default reducer
export default reducer;

View File

@ -2,4 +2,4 @@ module.exports.auth = {
bcrypt: {
rounds: 8
}
}
};

View File

@ -367,4 +367,4 @@ module.exports = {
}
}
};

View File

@ -366,4 +366,4 @@ module.exports = {
}
}
};

View File

@ -9,14 +9,14 @@
* https://sailsjs.com/config/http
*/
const rateLimit = require('express-rate-limit')
const rateLimit = require('express-rate-limit');
const rateLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 100, // limit each IP to 100 requests per windowMs
skip (req, res) {
return !req.path.startsWith('/api')
return !req.path.startsWith('/api');
}
})
});
module.exports.http = {
@ -69,4 +69,4 @@ module.exports.http = {
// return middlewareFn;
// })(),
}
}
};

View File

@ -6,4 +6,4 @@ module.exports.passport = {
local: {
strategy: require('passport-local').Strategy
}
}
};

View File

@ -34,4 +34,4 @@ module.exports.policies = {
TargetController: {
'*': [ 'sessionAuth' ]
}
}
};

View File

@ -1,6 +1,6 @@
// Passport protocol configurations
const crypto = require('crypto')
const base64URL = require('base64url')
const crypto = require('crypto');
const base64URL = require('base64url');
module.exports.protocols = {
local: {
@ -18,87 +18,87 @@ module.exports.protocols = {
*/
login: async function (req, identifier, password, next) {
if (!validateEmail(identifier)) {
return next(new Error('invalid email address'), false)
return next(new Error('invalid email address'), false);
}
try {
const user = await User.findOne({
email: identifier
})
if (!user) throw new Error('an account with that email was not found')
});
if (!user) {throw new Error('an account with that email was not found');}
const passport = await Passport.findOne({
protocol: 'local',
user: user.id
})
});
if (passport) {
const res = await Passport.validatePassword(password, passport)
if (!res) throw new Error('incorrect password')
return next(null, user)
const res = await Passport.validatePassword(password, passport);
if (!res) {throw new Error('incorrect password');}
return next(null, user);
} else {
throw new Error('that account does not have password login enabled')
throw new Error('that account does not have password login enabled');
}
} catch (e) {
return next(e, false)
return next(e, false);
}
},
register: async function (user, next) {
try {
const token = generateToken()
const password = user.password
if (!password.length) throw new Error('password cannot be blank')
delete user.password
const token = generateToken();
const password = user.password;
if (!password.length) {throw new Error('password cannot be blank');}
delete user.password;
const newUser = await User.create(user).fetch()
const newUser = await User.create(user).fetch();
try {
await Passport.create({
protocol: 'local',
password,
user: newUser.id,
accessToken: token
})
return next(null, token)
});
return next(null, token);
} catch (e) {
await User.destroy(newUser.id)
throw e
await User.destroy(newUser.id);
throw e;
}
} catch (e) {
return next(e)
return next(e);
}
},
update: async function (user, next) {
throw new Error('not implemented')
throw new Error('not implemented');
},
connect: async function (req, res, next) {
try {
const user = req.user
const password = req.param('password')
const user = req.user;
const password = req.param('password');
const pass = await Passport.findOne({
protocol: 'local',
user: user.id
})
});
if (!pass) {
await Passport.create({
protocol: 'local',
password,
user: user.id
})
});
} else {
return next(null, user)
return next(null, user);
}
} catch (e) {
return next(e)
return next(e);
}
}
}
}
};
const EMAIL_REGEX = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i
const EMAIL_REGEX = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
function validateEmail (email) {
return EMAIL_REGEX.test(email)
return EMAIL_REGEX.test(email);
}
function generateToken () {
return base64URL(crypto.randomBytes(48))
return base64URL(crypto.randomBytes(48));
}

View File

@ -79,4 +79,4 @@ module.exports.routes = {
// ╩ ╩╩╚═╝╚═╝
}
};

View File

@ -35,6 +35,7 @@
"babel-loader": "^8.0.4",
"babel-preset-es2015": "^6.24.1",
"css-loader": "^1.0.1",
"eslint": "^5.8.0",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.4.4",
"node-sass": "^4.9.4",

View File

@ -1,10 +1,10 @@
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const webpack = require('webpack')
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const path = require('path');
module.exports = (env, argv) => {
const mode = argv.mode || 'development'
const mode = argv.mode || 'development';
return {
mode: mode || 'development',
@ -45,5 +45,5 @@ module.exports = (env, argv) => {
'process.env.NODE_ENV': JSON.stringify(mode)
})
]
}
}
};
};