Initial content

This commit is contained in:
Adam Freeman
2024-05-29 18:54:49 +01:00
parent 2dbc1a8088
commit 5215a1d919
1944 changed files with 203378 additions and 0 deletions
@@ -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;