fix openldap init script paas user token saving,

add config file with BASE_DN,
add async wrapper class for ldap client,
implement addUser getUser delUser,
add and implement addGroup delGroup methods
This commit is contained in:
Arthur Lu 2023-11-17 19:49:11 +00:00
parent 0896ea4c81
commit 10116da900
7 changed files with 232 additions and 31 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
**/package-lock.json **/package-lock.json
**/node_modules **/node_modules
**/*.token **/*.token
**/config.json

View File

@ -0,0 +1,3 @@
{
"basedn": "dc=example,dc=com"
}

View File

@ -2,7 +2,7 @@ export BASE_DN=''
read -p "Base DN: " BASE_DN read -p "Base DN: " BASE_DN
export PAAS_PASSWD=$(tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_`{|}~' </dev/urandom | head -c 256; echo) export PAAS_PASSWD=$(tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_`{|}~' </dev/urandom | head -c 256; echo)
echo "$PAAS_PASSWD" > paas.token echo "$PAAS_PASSWD" -n > paas.token
echo "Saved PAAS Authentication Token (password) to paas.token" echo "Saved PAAS Authentication Token (password) to paas.token"
envsubst '$BASE_DN' < auth.template.ldif > auth.ldif envsubst '$BASE_DN' < auth.template.ldif > auth.ldif

11
src/config.js Normal file
View File

@ -0,0 +1,11 @@
import { readFileSync } from "fs";
import { exit } from "process";
export default () => {
try {
return JSON.parse(readFileSync(global.argv.configPath));
}
catch (e) {
console.log(`Error: ${global.argv.configPath} was not found. Please follow the directions in the README to initialize localdb.json.`);
exit(1);
}
};

View File

@ -1,31 +1,179 @@
import ldap from "ldapjs"; import ldap from "ldapjs";
import { exit } from "process";
export class LDAP { export default class LDAP {
#client = null; #client = null;
#paasBind = null; #basedn = null;
#baseDN = null; #peopledn = null;
#groupsdn = null;
constructor (url, paasBind, baseDN) { constructor (url, basedn) {
const opts = { const opts = {
url url
}; };
this.#client = new LDAPJS_CLIENT_ASYNC_WRAPPER(opts);
this.#basedn = basedn;
this.#peopledn = `ou=people,${basedn}`;
this.#groupsdn = `ou=groups,${basedn}`;
}
async addUser (bind, uid, attrs) {
const result = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) {
return result;
}
const userDN = `uid=${uid},${this.#peopledn}`;
const entry = {
objectClass: "inetOrgPerson",
cn: attrs.cn,
sn: attrs.sn,
uid,
userPassword: attrs.userPassword
};
return await this.#client.add(userDN, entry);
}
async getUser (bind, uid) {
const result = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) {
return result;
}
const opts = {
filter: `(uid=${uid})`,
scope: "sub"
};
return await this.#client.search(this.#peopledn, opts);
}
async modUser (bind, uid, attrs) { }
async delUser (bind, uid) {
const result = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) {
return result;
}
const userDN = `uid=${uid},${this.#peopledn}`;
return await this.#client.del(userDN);
}
async addGroup (bind, gid, attrs) {
const result = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) {
return result;
}
const groupDN = `cn=${gid},${this.#groupsdn}`;
const entry = {
objectClass: "groupOfNames",
member: "",
cn: gid
};
return await this.#client.add(groupDN, entry);
}
async delGroup (bind, gid) {
const result = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) {
return result;
}
const groupDN = `cn=${gid},${this.#groupsdn}`;
return await this.#client.del(groupDN);
}
async addUserToGroup (bind, uid, gid) {
}
async delUserFromGroup (bind, uid, gid) { }
}
class LDAPJS_CLIENT_ASYNC_WRAPPER {
#client = null;
constructor (opts) {
this.#client = ldap.createClient(opts); this.#client = ldap.createClient(opts);
this.#client.on("connectError", (err) => { this.#client.on("error", (err) => {
console.err(`Error: could not establish connection to ${url}`); console.error(`An error occured:\n${err}`);
console.err(err); });
exit(1); this.#client.on("connectError", (err) => {
console.error(`Unable to connect to ${opts.url}:\n${err}`);
}); });
this.#paasBind = paasBind;
this.#baseDN = baseDN;
} }
addUser (uid, entry) {} bind (dn, password) {
return new Promise((resolve) => {
getUser (uid) {} this.#client.bind(dn, password, (err) => {
if (err) {
modUser (uid, entry) {} resolve({ ok: false, error: err });
}
delUser (uid) {} else {
resolve({ ok: true });
}
});
});
}
add (dn, entry) {
return new Promise((resolve) => {
this.#client.add(dn, entry, (err) => {
if (err) {
resolve({ ok: false, error: err });
}
else {
resolve({ ok: true });
}
});
});
}
search (base, options) {
return new Promise((resolve) => {
this.#client.search(base, options, (err, res) => {
if (err) {
return resolve({ ok: false, error: err });
}
const results = { ok: false, status: 1, message: "", entries: [] };
res.on("searchRequest", (searchRequest) => { });
res.on("searchEntry", (entry) => {
results.entries.push({ dn: entry.pojo.objectName, attributes: entry.pojo.attributes });
});
res.on("searchReference", (referral) => { });
res.on("error", (error) => {
results.ok = error.status === 0;
results.status = error.status;
results.message = error.message;
resolve(results);
});
res.on("end", (result) => {
results.ok = result.status === 0;
results.status = result.status;
results.message = result.message;
resolve(results);
});
});
});
}
modify (name, changes) {
return new Promise((resolve) => {
this.#client.modify(name, changes, (err) => {
if (err) {
resolve({ ok: false, error: err });
}
else {
resolve({ ok: true });
}
});
});
}
del (dn) {
return new Promise((resolve) => {
this.#client.del(dn, (err) => {
if (err) {
resolve({ ok: false, error: err });
}
else {
resolve({ ok: true });
}
});
});
}
} }

View File

@ -2,29 +2,56 @@ 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 LDAP from "ldap.js";
import LDAP from "./ldap.js";
import _config from "./config.js";
import _package from "./package.js";
import parseArgs from "minimist";
global.argv = parseArgs(process.argv.slice(2), {
default: {
package: "package.json",
listenPort: 8082,
ldapURL: "ldap://localhost",
configPath: "config/config.json"
}
});
global.package = _package(global.argv.package);
global.config = _config(global.argv.configPath);
const ldap = new LDAP(global.argv.ldapURL, global.config.basedn);
/* import { readFileSync } from "fs";
const paas = {
dn: `uid=paas,ou=people,${global.config.basedn}`,
password: readFileSync("paas.token").toString()
};
console.log(await ldap.addUser(paas, "testuser", { cn: "test", sn: "test", userPassword: "test" }));
console.log((await ldap.getUser(paas, "testuser")).entries[0].attributes);
console.log(await ldap.delUser(paas, "testuser"));
console.log(await ldap.addGroup(paas, "testgroup"));
console.log(await ldap.delGroup(paas, "testgroup"));
exit(0); */
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.listen(global.db.listenPort, () => { app.listen(global.argv.listenPort, () => {
console.log(`proxmoxaas-api v${global.api.version} listening on port ${global.db.listenPort}`); console.log(`proxmoxaas-ldap v${global.package.version} listening on port ${global.argv.listenPort}`);
}); });
app.get("/:user", (req, res) => { app.get("/:user", async (req, res) => {
}); });
app.post("/:user", (req, res) => { app.post("/:user", async (req, res) => {
}); });
app.delete("/:user", (req, res) => { app.delete("/:user", async (req, res) => {
}); });
app.post("/:user/password", (req, res) => { app.post("/:user/password", async (req, res) => {
}); });

11
src/package.js Normal file
View File

@ -0,0 +1,11 @@
import { readFileSync } from "fs";
import { exit } from "process";
export default (path) => {
try {
return JSON.parse(readFileSync(path));
}
catch (e) {
console.log(`Error: ${path} was not found.`);
exit(1);
}
};