add endpoints for session creation,

update package.json
This commit is contained in:
Arthur Lu 2024-01-09 20:05:21 +00:00
parent ec6eb5ec8b
commit 8edfbe1ace
4 changed files with 219 additions and 198 deletions

View File

@ -1,3 +1,10 @@
{ {
"basedn": "dc=example,dc=com" "basedn": "dc=example,dc=com",
"sessionSecretKey": "super secret key",
"sessionCookie": {
"path": "/",
"httpOnly": true,
"secure": false,
"maxAge": 7200000
}
} }

View File

@ -11,6 +11,7 @@
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.18.2", "express": "^4.18.2",
"express-session": "^1.17.3",
"ldapjs": "^3.0.5", "ldapjs": "^3.0.5",
"minimist": "^1.2.8", "minimist": "^1.2.8",
"morgan": "^1.10.0" "morgan": "^1.10.0"
@ -20,13 +21,7 @@
"eslint-config-standard": "^17.1.0", "eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^16.0.1", "eslint-plugin-n": "^16.0.1",
"eslint-plugin-promise": "^6.1.1", "eslint-plugin-promise": "^6.1.1"
"body-parser": "^1.20.1",
"cookie": "^0.5.0",
"cookie-parser": "^1.4.6",
"express": "^4.18.2",
"minimist": "^1.2.8",
"morgan": "^1.10.0"
}, },
"scripts": { "scripts": {
"lint": "DEBUG=eslint:cli-engine eslint --fix ." "lint": "DEBUG=eslint:cli-engine eslint --fix ."

View File

@ -16,18 +16,11 @@ export default class LDAP {
this.#groupsdn = `ou=groups,${basedn}`; this.#groupsdn = `ou=groups,${basedn}`;
} }
createUserBind (uid, password) { async bindUser (uid, password) {
return { return await this.#client.bind(`uid=${uid},${this.#peopledn}`, password);
dn: `uid=${uid},${this.#peopledn}`,
password
};
} }
async getAllUsers (bind) { async getAllUsers () {
const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!bindResult.ok) {
return bindResult;
}
const result = await this.#client.search(this.#peopledn, { const result = await this.#client.search(this.#peopledn, {
scope: "one" scope: "one"
}); });
@ -35,12 +28,7 @@ export default class LDAP {
return result; return result;
} }
async addUser (bind, uid, attrs) { async addUser (uid, attrs) {
const logger = new LDAP_MULTIOP_LOGGER(`add ${uid}`);
const bindResult = await this.#client.bind(bind.dn, bind.password, logger);
if (!bindResult.ok) {
return bindResult;
}
const userDN = `uid=${uid},${this.#peopledn}`; const userDN = `uid=${uid},${this.#peopledn}`;
if (!attrs.cn || !attrs.sn || !attrs.userPassword) { if (!attrs.cn || !attrs.sn || !attrs.userPassword) {
return { return {
@ -59,26 +47,17 @@ export default class LDAP {
uid, uid,
userPassword: attrs.userPassword userPassword: attrs.userPassword
}; };
await this.#client.add(userDN, entry, logger); return await this.#client.add(userDN, entry);
return logger;
} }
async getUser (bind, uid) { async getUser (uid) {
const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!bindResult.ok) {
return bindResult;
}
const result = await this.#client.search(`uid=${uid},${this.#peopledn}`, {}); const result = await this.#client.search(`uid=${uid},${this.#peopledn}`, {});
result.user = result.entries[0]; // assume there should only be 1 entry result.user = result.entries[0]; // assume there should only be 1 entry
return result; return result;
} }
async modUser (bind, uid, newAttrs) { async modUser (uid, newAttrs) {
const logger = new LDAP_MULTIOP_LOGGER(`modify ${uid}`); const logger = new LDAP_MULTIOP_LOGGER(`modify ${uid}`);
const bindResult = await this.#client.bind(bind.dn, bind.password, logger);
if (!bindResult.ok) {
return bindResult;
}
for (const attr of ["cn", "sn", "userPassword"]) { for (const attr of ["cn", "sn", "userPassword"]) {
if (attr in newAttrs && newAttrs[attr]) { // attr should exist and not be undefined or null if (attr in newAttrs && newAttrs[attr]) { // attr should exist and not be undefined or null
const change = new ldap.Change({ const change = new ldap.Change({
@ -94,12 +73,8 @@ export default class LDAP {
return logger; return logger;
} }
async delUser (bind, uid) { async delUser (uid) {
const logger = new LDAP_MULTIOP_LOGGER(`del ${uid}`); const logger = new LDAP_MULTIOP_LOGGER(`del ${uid}`);
const bindResult = await this.#client.bind(bind.dn, bind.password, logger);
if (!bindResult.ok) {
return bindResult;
}
const userDN = `uid=${uid},${this.#peopledn}`; const userDN = `uid=${uid},${this.#peopledn}`;
await this.#client.del(userDN, logger); await this.#client.del(userDN, logger);
const groups = await this.#client.search(this.#groupsdn, { const groups = await this.#client.search(this.#groupsdn, {
@ -122,11 +97,7 @@ export default class LDAP {
return logger; return logger;
} }
async getAllGroups (bind) { async getAllGroups () {
const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!bindResult.ok) {
return bindResult;
}
const result = await this.#client.search(this.#groupsdn, { const result = await this.#client.search(this.#groupsdn, {
scope: "one" scope: "one"
}); });
@ -134,49 +105,28 @@ export default class LDAP {
return result; return result;
} }
async addGroup (bind, gid) { async addGroup (gid) {
const logger = new LDAP_MULTIOP_LOGGER(`add ${gid}`);
const bindResult = await this.#client.bind(bind.dn, bind.password, logger);
if (!bindResult.ok) {
return bindResult;
}
const groupDN = `cn=${gid},${this.#groupsdn}`; const groupDN = `cn=${gid},${this.#groupsdn}`;
const entry = { const entry = {
objectClass: "groupOfNames", objectClass: "groupOfNames",
member: "", member: "",
cn: gid cn: gid
}; };
await this.#client.add(groupDN, entry, logger); return await this.#client.add(groupDN, entry);
return logger;
} }
async getGroup (bind, gid) { async getGroup (gid) {
const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!bindResult.ok) {
return bindResult;
}
const result = await this.#client.search(`cn=${gid},${this.#groupsdn}`, {}); const result = await this.#client.search(`cn=${gid},${this.#groupsdn}`, {});
result.group = result.entries[0]; // assume there should only be 1 entry result.group = result.entries[0]; // assume there should only be 1 entry
return result; return result;
} }
async delGroup (bind, gid) { async delGroup (gid) {
const logger = new LDAP_MULTIOP_LOGGER(`del ${gid}`);
const bindResult = await this.#client.bind(bind.dn, bind.password, logger);
if (!bindResult.ok) {
return bindResult;
}
const groupDN = `cn=${gid},${this.#groupsdn}`; const groupDN = `cn=${gid},${this.#groupsdn}`;
await this.#client.del(groupDN, logger); return await this.#client.del(groupDN);
return logger;
} }
async addUserToGroup (bind, uid, gid) { async addUserToGroup (uid, gid) {
const logger = new LDAP_MULTIOP_LOGGER(`add ${uid} to ${gid}`);
const bindResult = await this.#client.bind(bind.dn, bind.password, logger);
if (!bindResult.ok) {
return bindResult;
}
// add the user // add the user
const change = new ldap.Change({ const change = new ldap.Change({
operation: "add", operation: "add",
@ -185,16 +135,10 @@ export default class LDAP {
values: [`uid=${uid},${this.#peopledn}`] values: [`uid=${uid},${this.#peopledn}`]
} }
}); });
await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change, logger); return await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change);
return logger;
} }
async delUserFromGroup (bind, uid, gid) { async delUserFromGroup (uid, gid) {
const logger = new LDAP_MULTIOP_LOGGER(`del ${uid} from ${gid}`);
const bindResult = await this.#client.bind(bind.dn, bind.password, logger);
if (!bindResult.ok) {
return bindResult;
}
const change = new ldap.Change({ const change = new ldap.Change({
operation: "delete", operation: "delete",
modification: { modification: {
@ -202,8 +146,7 @@ export default class LDAP {
values: [`uid=${uid},${this.#peopledn}`] values: [`uid=${uid},${this.#peopledn}`]
} }
}); });
await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change, logger); return await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change);
return logger;
} }
} }

View File

@ -2,6 +2,7 @@ import express from "express";
import bodyParser from "body-parser"; import bodyParser from "body-parser";
import cookieParser from "cookie-parser"; import cookieParser from "cookie-parser";
import morgan from "morgan"; import morgan from "morgan";
import session from "express-session";
import LDAP from "./ldap.js"; import LDAP from "./ldap.js";
import _config from "./config.js"; import _config from "./config.js";
@ -21,12 +22,19 @@ global.argv = parseArgs(process.argv.slice(2), {
global.package = _package(global.argv.package); global.package = _package(global.argv.package);
global.config = _config(global.argv.configPath); global.config = _config(global.argv.configPath);
const ldap = new LDAP(global.argv.ldapURL, global.config.basedn); const LDAPSessions = {};
const app = express(); const app = express();
app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser()); app.use(cookieParser());
app.use(morgan("combined")); app.use(morgan("combined"));
app.use(session({
secret: global.config.sessionSecretKey,
name: global.config.sessionCookieName,
cookie: global.config.sessionCookie,
resave: false,
saveUninitialized: true
}));
app.listen(global.argv.listenPort, () => { app.listen(global.argv.listenPort, () => {
console.log(`proxmoxaas-api v${global.package.version} listening on port ${global.argv.listenPort}`); console.log(`proxmoxaas-api v${global.package.version} listening on port ${global.argv.listenPort}`);
@ -50,16 +58,53 @@ app.get("/echo", (req, res) => {
res.status(200).send({ body: req.body, cookies: req.cookies }); res.status(200).send({ body: req.body, cookies: req.cookies });
}); });
app.get("/users", async (req, res) => { /**
* POST - get session ticket by authenticating using user id and password
*/
app.post("/ticket", async (req, res) => {
const params = { const params = {
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass) uid: req.body.uid,
password: req.body.password
}; };
const result = await ldap.getAllUsers(params.bind); const newLDAPSession = new LDAP(global.argv.ldapURL, global.config.basedn);
const bindResult = await newLDAPSession.bindUser(params.uid, params.password);
if (bindResult.ok) {
LDAPSessions[req.session.id] = newLDAPSession;
res.status(200).send({ auth: true });
}
else {
res.send({
ok: bindResult.ok,
error: bindResult.error
});
}
});
/**
* DELETE - invalidate and remove session ticket
*/
app.delete("/ticket", async (req, res) => {
req.session.ldap = null;
req.session.destroy();
res.send({ auth: false });
});
/**
* GET - get user attributes for all users
*/
app.get("/users", async (req, res) => {
if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.getAllUsers();
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error, error: result.error,
users: result.users users: result.users
}); });
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
@ -69,29 +114,28 @@ app.get("/users", async (req, res) => {
* - cn: common name * - cn: common name
* - sn: surname * - sn: surname
* - userpassword: user password * - userpassword: user password
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.post("/users/:userid", async (req, res) => { app.post("/users/:userid", async (req, res) => {
const params = { const params = {
userid: req.params.userid, userid: req.params.userid,
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass),
userattrs: { userattrs: {
cn: req.body.usercn, cn: req.body.usercn,
sn: req.body.usersn, sn: req.body.usersn,
userPassword: req.body.userpassword userPassword: req.body.userpassword
} }
}; };
const checkUser = await ldap.getUser(params.bind, params.userid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const checkUser = await ldap.getUser(params.userid);
if (!checkUser.ok && checkUser.error.code === 32) { // the user does not exist, create new user if (!checkUser.ok && checkUser.error.code === 32) { // the user does not exist, create new user
const result = await ldap.addUser(params.bind, params.userid, params.userattrs); const result = await ldap.addUser(params.userid, params.userattrs);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error error: result.error
}); });
} }
else if (checkUser.ok) { // the user does exist, modify the user entries else if (checkUser.ok) { // the user does exist, modify the user entries
const result = await ldap.modUser(params.bind, params.userid, params.userattrs); const result = await ldap.modUser(params.userid, params.userattrs);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error error: result.error
@ -103,21 +147,24 @@ app.post("/users/:userid", async (req, res) => {
error: checkUser.error error: checkUser.error
}); });
} }
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
* GET - get user attributes * GET - get user attributes
* request: * request:
* - userid: user id * - userid: user id
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.get("/users/:userid", async (req, res) => { app.get("/users/:userid", async (req, res) => {
const params = { const params = {
userid: req.params.userid, userid: req.params.userid
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass)
}; };
const result = await ldap.getUser(params.bind, params.userid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.getUser(params.userid);
if (result.ok) { if (result.ok) {
res.send({ res.send({
ok: result.ok, ok: result.ok,
@ -131,71 +178,87 @@ app.get("/users/:userid", async (req, res) => {
error: result.error error: result.error
}); });
} }
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
* DELETE - delete user * DELETE - delete user
* request: * request:
* - userid: user id * - userid: user id
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.delete("/users/:userid", async (req, res) => { app.delete("/users/:userid", async (req, res) => {
const params = { const params = {
userid: req.params.userid, userid: req.params.userid
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass)
}; };
const result = await ldap.delUser(params.bind, params.userid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.delUser(params.userid);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error error: result.error
}); });
}
else {
res.status(403).send({ auth: false });
}
}); });
/**
* GET - get group attributes including members for all groups
* request:
*/
app.get("/groups", async (req, res) => { app.get("/groups", async (req, res) => {
const params = { if (req.session.id in LDAPSessions) {
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass) const ldap = LDAPSessions[req.session.id];
}; const result = await ldap.getAllGroups();
const result = await ldap.getAllGroups(params.bind);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error, error: result.error,
groups: result.groups groups: result.groups
}); });
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
* POST - create a new group * POST - create a new group
* request: * request:
* - groupid: group id * - groupid: group id
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.post("/groups/:groupid", async (req, res) => { app.post("/groups/:groupid", async (req, res) => {
const params = { const params = {
groupid: req.params.groupid, groupid: req.params.groupid
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass)
}; };
const result = await ldap.addGroup(params.bind, params.groupid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.addGroup(params.groupid);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error error: result.error
}); });
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
* GET - get group attributes including members * GET - get group attributes including members
* request: * request:
* - groupid: group id * - groupid: group id
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.get("/groups/:groupid", async (req, res) => { app.get("/groups/:groupid", async (req, res) => {
const params = { const params = {
groupid: req.params.groupid, groupid: req.params.groupid
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass)
}; };
const result = await ldap.getGroup(params.bind, params.groupid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.getGroup(params.groupid);
if (result.ok) { if (result.ok) {
res.send({ res.send({
ok: result.ok, ok: result.ok,
@ -209,25 +272,32 @@ app.get("/groups/:groupid", async (req, res) => {
error: result.error error: result.error
}); });
} }
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
* DELETE - delete group * DELETE - delete group
* request: * request:
* - groupid: group id * - groupid: group id
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.delete("/groups/:groupid", async (req, res) => { app.delete("/groups/:groupid", async (req, res) => {
const params = { const params = {
groupid: req.params.groupid, groupid: req.params.groupid
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass)
}; };
const result = await ldap.delGroup(params.bind, params.groupid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.delGroup(params.groupid);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error error: result.error
}); });
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
@ -235,38 +305,44 @@ app.delete("/groups/:groupid", async (req, res) => {
* request: * request:
* - groupid: group id * - groupid: group id
* - userid: user id * - userid: user id
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.post("/groups/:groupid/members/:userid", async (req, res) => { app.post("/groups/:groupid/members/:userid", async (req, res) => {
const params = { const params = {
groupid: req.params.groupid, groupid: req.params.groupid,
userid: req.params.userid, userid: req.params.userid
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass)
}; };
const result = await ldap.addUserToGroup(params.bind, params.userid, params.groupid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.addUserToGroup(params.userid, params.groupid);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error error: result.error
}); });
}
else {
res.status(403).send({ auth: false });
}
}); });
/** /**
* DELETE - remove a member from the group * DELETE - remove a member from the group
* - groupid: group id * - groupid: group id
* - userid: user id * - userid: user id
* - binduser: bind user id
* - bindpass: bind user password
*/ */
app.delete("/groups/:groupid/members/:userid", async (req, res) => { app.delete("/groups/:groupid/members/:userid", async (req, res) => {
const params = { const params = {
groupid: req.params.groupid, groupid: req.params.groupid,
userid: req.params.userid, userid: req.params.userid
bind: ldap.createUserBind(req.body.binduser, req.body.bindpass)
}; };
const result = await ldap.delUserFromGroup(params.bind, params.userid, params.groupid); if (req.session.id in LDAPSessions) {
const ldap = LDAPSessions[req.session.id];
const result = await ldap.delUserFromGroup(params.userid, params.groupid);
res.send({ res.send({
ok: result.ok, ok: result.ok,
error: result.error error: result.error
}); });
}
else {
res.status(403).send({ auth: false });
}
}); });