Initial content
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.roleHook = exports.roleGuard = exports.createAuth = void 0;
|
||||
const orm_authstore_1 = require("./orm_authstore");
|
||||
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
||||
const passport_1 = __importDefault(require("passport"));
|
||||
const passport_config_1 = require("./passport_config");
|
||||
const jwt_secret = "mytokensecret";
|
||||
const store = new orm_authstore_1.OrmAuthStore();
|
||||
const createAuth = (app) => {
|
||||
(0, passport_config_1.configurePassport)({ store, jwt_secret });
|
||||
app.get("/signin", (req, resp) => {
|
||||
const data = {
|
||||
// username: req.query["username"],
|
||||
// password: req.query["password"],
|
||||
failed: req.query["failed"] ? true : false,
|
||||
signinpage: true
|
||||
};
|
||||
resp.render("signin", data);
|
||||
});
|
||||
app.post("/signin", passport_1.default.authenticate("local", {
|
||||
failureRedirect: `/signin?failed=1`,
|
||||
successRedirect: "/"
|
||||
}));
|
||||
app.use(passport_1.default.authenticate("session"), (req, resp, next) => {
|
||||
resp.locals.user = req.user;
|
||||
resp.locals.authenticated
|
||||
= req.authenticated = req.user !== undefined;
|
||||
next();
|
||||
});
|
||||
app.post("/api/signin", async (req, resp) => {
|
||||
const username = req.body.username;
|
||||
const password = req.body.password;
|
||||
const result = {
|
||||
success: await store.validateCredentials(username, password)
|
||||
};
|
||||
if (result.success) {
|
||||
result.token = jsonwebtoken_1.default.sign({ username }, jwt_secret, { expiresIn: "1hr" });
|
||||
}
|
||||
resp.json(result);
|
||||
resp.end();
|
||||
});
|
||||
app.post("/signout", async (req, resp) => {
|
||||
req.session.destroy(() => {
|
||||
resp.redirect("/");
|
||||
});
|
||||
});
|
||||
app.get("/unauthorized", async (req, resp) => {
|
||||
resp.render("unauthorized");
|
||||
});
|
||||
};
|
||||
exports.createAuth = createAuth;
|
||||
const roleGuard = (role) => {
|
||||
return async (req, resp, next) => {
|
||||
if (req.authenticated) {
|
||||
const username = req.user?.username;
|
||||
if (username != undefined
|
||||
&& await store.validateMembership(username, role)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
resp.redirect("/unauthorized");
|
||||
}
|
||||
else {
|
||||
resp.redirect("/signin");
|
||||
}
|
||||
};
|
||||
};
|
||||
exports.roleGuard = roleGuard;
|
||||
const roleHook = (role) => {
|
||||
return async (ctx) => {
|
||||
if (!ctx.params.authenticated) {
|
||||
ctx.http = { status: 401 };
|
||||
ctx.result = {};
|
||||
}
|
||||
else if (!(await store.validateMembership(ctx.params.user.username, role))) {
|
||||
ctx.http = { status: 403 };
|
||||
ctx.result = {};
|
||||
}
|
||||
};
|
||||
};
|
||||
exports.roleHook = roleHook;
|
||||
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.initializeAuthModels = exports.RoleModel = exports.CredentialsModel = void 0;
|
||||
const sequelize_1 = require("sequelize");
|
||||
class CredentialsModel extends sequelize_1.Model {
|
||||
}
|
||||
exports.CredentialsModel = CredentialsModel;
|
||||
class RoleModel extends sequelize_1.Model {
|
||||
}
|
||||
exports.RoleModel = RoleModel;
|
||||
const initializeAuthModels = (sequelize) => {
|
||||
CredentialsModel.init({
|
||||
username: { type: sequelize_1.DataTypes.STRING, primaryKey: true },
|
||||
hashedPassword: { type: sequelize_1.DataTypes.BLOB },
|
||||
salt: { type: sequelize_1.DataTypes.BLOB }
|
||||
}, { sequelize });
|
||||
RoleModel.init({
|
||||
name: { type: sequelize_1.DataTypes.STRING, primaryKey: true },
|
||||
}, { sequelize });
|
||||
RoleModel.belongsToMany(CredentialsModel, { through: "RoleMembershipJunction", foreignKey: "name" });
|
||||
CredentialsModel.belongsToMany(RoleModel, { through: "RoleMembershipJunction", foreignKey: "username" });
|
||||
};
|
||||
exports.initializeAuthModels = initializeAuthModels;
|
||||
@@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.OrmAuthStore = void 0;
|
||||
const sequelize_1 = require("sequelize");
|
||||
const orm_auth_models_1 = require("./orm_auth_models");
|
||||
const crypto_1 = require("crypto");
|
||||
class OrmAuthStore {
|
||||
sequelize;
|
||||
constructor() {
|
||||
this.sequelize = new sequelize_1.Sequelize({
|
||||
dialect: "sqlite",
|
||||
storage: "orm_auth.db",
|
||||
logging: console.log,
|
||||
logQueryParameters: true
|
||||
});
|
||||
this.initModelAndDatabase();
|
||||
}
|
||||
async initModelAndDatabase() {
|
||||
(0, orm_auth_models_1.initializeAuthModels)(this.sequelize);
|
||||
await this.sequelize.drop();
|
||||
await this.sequelize.sync();
|
||||
await this.storeOrUpdateUser("alice", "mysecret");
|
||||
await this.storeOrUpdateUser("bob", "mysecret");
|
||||
await this.storeOrUpdateRole({
|
||||
name: "Users", members: ["alice", "bob"]
|
||||
});
|
||||
await this.storeOrUpdateRole({
|
||||
name: "Admins", members: ["alice"]
|
||||
});
|
||||
}
|
||||
async getUser(name) {
|
||||
return await orm_auth_models_1.CredentialsModel.findByPk(name);
|
||||
}
|
||||
async storeOrUpdateUser(username, password) {
|
||||
const salt = (0, crypto_1.randomBytes)(16);
|
||||
const hashedPassword = await this.createHashCode(password, salt);
|
||||
const [model] = await orm_auth_models_1.CredentialsModel.upsert({
|
||||
username, hashedPassword, salt
|
||||
});
|
||||
return model;
|
||||
}
|
||||
async validateCredentials(username, password) {
|
||||
const storedCreds = await this.getUser(username);
|
||||
if (storedCreds) {
|
||||
const candidateHash = await this.createHashCode(password, storedCreds.salt);
|
||||
return (0, crypto_1.timingSafeEqual)(candidateHash, storedCreds.hashedPassword);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
createHashCode(password, salt) {
|
||||
return new Promise((resolve, reject) => {
|
||||
(0, crypto_1.pbkdf2)(password, salt, 100000, 64, "sha512", (err, hash) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
;
|
||||
resolve(hash);
|
||||
});
|
||||
});
|
||||
}
|
||||
async getRole(name) {
|
||||
const stored = await orm_auth_models_1.RoleModel.findByPk(name, {
|
||||
include: [{ model: orm_auth_models_1.CredentialsModel, attributes: ["username"] }]
|
||||
});
|
||||
if (stored) {
|
||||
return {
|
||||
name: stored.name,
|
||||
members: stored.CredentialsModels?.map(m => m.username) ?? []
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async getRolesForUser(username) {
|
||||
return (await orm_auth_models_1.RoleModel.findAll({
|
||||
include: [{
|
||||
model: orm_auth_models_1.CredentialsModel,
|
||||
where: { username },
|
||||
attributes: []
|
||||
}]
|
||||
})).map(rm => rm.name);
|
||||
}
|
||||
async storeOrUpdateRole(role) {
|
||||
return await this.sequelize.transaction(async (transaction) => {
|
||||
const users = await orm_auth_models_1.CredentialsModel.findAll({
|
||||
where: { username: { [sequelize_1.Op.in]: role.members } },
|
||||
transaction
|
||||
});
|
||||
const [rm] = await orm_auth_models_1.RoleModel.findOrCreate({
|
||||
where: { name: role.name }, transaction
|
||||
});
|
||||
await rm.setCredentialsModels(users, { transaction });
|
||||
return role;
|
||||
});
|
||||
}
|
||||
async validateMembership(username, rolename) {
|
||||
return (await this.getRolesForUser(username)).includes(rolename);
|
||||
}
|
||||
}
|
||||
exports.OrmAuthStore = OrmAuthStore;
|
||||
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.configurePassport = void 0;
|
||||
const passport_1 = __importDefault(require("passport"));
|
||||
const passport_local_1 = require("passport-local");
|
||||
const passport_jwt_1 = require("passport-jwt");
|
||||
const configurePassport = (config) => {
|
||||
passport_1.default.use(new passport_local_1.Strategy(async (username, password, callback) => {
|
||||
if (await config.store.validateCredentials(username, password)) {
|
||||
return callback(null, { username });
|
||||
}
|
||||
return callback(null, false);
|
||||
}));
|
||||
passport_1.default.use(new passport_jwt_1.Strategy({
|
||||
jwtFromRequest: passport_jwt_1.ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKey: config.jwt_secret
|
||||
}, (payload, callback) => {
|
||||
return callback(null, { username: payload.username });
|
||||
}));
|
||||
passport_1.default.serializeUser((user, callback) => {
|
||||
callback(null, user);
|
||||
});
|
||||
passport_1.default.deserializeUser((user, callback) => {
|
||||
callback(null, user);
|
||||
});
|
||||
};
|
||||
exports.configurePassport = configurePassport;
|
||||
Reference in New Issue
Block a user