-
-
Save justsml/ab813f9208009befbfa3890028d67559 to your computer and use it in GitHub Desktop.
Minor updates
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Easier to follow. Equally easy to test -- and with one fewer function in need of testing. | |
import { userInfo } from "os"; | |
const Promise = require("bluebird"); | |
const { hashStringAsync } = Promise.promisifyAll(require("./lib/crypto")); | |
const { logEventAsync } = Promise.promisifyAll(require("./lib/log")); | |
const { openAsync } = Promise.promisifyAll(require("./lib/db")); | |
const { TimeoutError, ValidationError, NotFoundError } = require("./errors"); | |
const _openHandle = openAsync(); // FYI: Promises include memoization (caching) built into same API | |
module.exports = { auth }; | |
/* auth is our main function */ | |
async function auth({ | |
username, | |
password, | |
_onTimeoutError = errorHandler, | |
_onNotFoundError = errorHandler, | |
_onValidationError = errorHandler, | |
}) { | |
try { | |
// No more reusing "password" as "hashed password". | |
// Still starting the hash ASAP and not waiting for it. | |
const hashedPasswordPromise = hashStringAsync(password); | |
authValidate({ username, password }); | |
const { users, admin } = await getModels(); | |
const user = await users.findOneAsync({ | |
username, | |
// I would have renamed "password" here; bad form to have a field named | |
// password that's expecting a hash. | |
password: await hashedPasswordPromise, // Deferred await to when we need it. | |
}); | |
// Inventing a side tributary: If a user is an admin, then grab their admin | |
// permissions. | |
await userFound(user); | |
return loadAdmin(user); | |
} catch (e) { | |
// Left this in because Bluebird's extension for matching errors is cute. | |
// It's super easy to forget the `await` here - it changes how functions work in pretty substantial, yet non-obvious ways. | |
// if the custom handlers (_onTimeoutError) fail to output the error info, they wouldn't be able re-throw errors to be caught higher up | |
await Promise.reject(e) // I agree, it is pretty cute :) | |
.catch(TimeoutError, _onTimeoutError) | |
.catch(NotFoundError, _onNotFoundError) | |
.catch(ValidationError, _onValidationError) | |
.catch(errorHandler); | |
} | |
} | |
async function loadAdmin(user) { | |
if (user.admin) { | |
const admin = await admin.findOneAsync({ id: user.id }); | |
user.adminPermissions = admin.permissions; | |
} | |
return user; | |
} | |
function authValidate({ username, password }) { | |
if (!username || username.length < 4) { | |
throw new ValidationError("Invalid username. Required, 4 char minimum."); | |
} | |
if (!password || password.length < 4) { | |
throw new ValidationError("Invalid password. Required, 4 char minimum."); | |
} | |
} | |
function userFound(results) { | |
if (results) return; | |
throw new NotFoundError("No users matched. Login failed"); | |
} | |
function getModels() { | |
return _openHandle.then(({ models }) => models); | |
} | |
function errorHandler(err) { | |
console.error("Failed auth!", err); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment