From 77c556aa67f49781b432e43ac03e832567b95be3 Mon Sep 17 00:00:00 2001 From: Arthur Lu Date: Fri, 15 Dec 2023 23:20:43 +0000 Subject: [PATCH] remove admin userPassword read permission, implement user add mod get del endpoints --- openldap/auth.template.ldif | 2 +- src/ldap.js | 25 ++++++++- src/main.js | 106 +++++++++++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/openldap/auth.template.ldif b/openldap/auth.template.ldif index def5240..7f388b7 100644 --- a/openldap/auth.template.ldif +++ b/openldap/auth.template.ldif @@ -5,7 +5,7 @@ delete: olcAccess - add: olcAccess olcAccess: {0}to attrs=userPassword - by group/groupOfNames/member="cn=admins,ou=groups,$BASE_DN" write + by group/groupOfNames/member="cn=admins,ou=groups,$BASE_DN" =wcdx by self write by anonymous auth by * none diff --git a/src/ldap.js b/src/ldap.js index b265a71..39ab5c9 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -16,6 +16,13 @@ export default class LDAP { this.#groupsdn = `ou=groups,${basedn}`; } + createUserBind (uid, password) { + return { + dn: `uid=${uid},${this.#peopledn}`, + password + }; + } + async addUser (bind, uid, attrs) { const logger = new LDAP_MULTIOP_LOGGER(`add ${uid}`); await this.#client.bind(bind.dn, bind.password, logger); @@ -23,6 +30,16 @@ export default class LDAP { return logger; } const userDN = `uid=${uid},${this.#peopledn}`; + if (!attrs.cn || !attrs.sn || !attrs.userPassword) { + return { + ok: false, + error: { + code: 100, + name: "UndefinedAttributeValueError", + message: "Undefined Attribute Value" + } + }; + } const entry = { objectClass: "inetOrgPerson", cn: attrs.cn, @@ -39,7 +56,9 @@ export default class LDAP { if (!bindResult.ok) { return bindResult; } - return 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 + return result; } async modUser (bind, uid, newAttrs) { @@ -49,7 +68,7 @@ export default class LDAP { return logger; } for (const attr of ["cn", "sn", "userPassword"]) { - if (attr in newAttrs) { + if (attr in newAttrs && newAttrs[attr]) { // attr should exist and not be undefined or null const change = new ldap.Change({ operation: "replace", modification: { @@ -232,7 +251,7 @@ class LDAPJS_CLIENT_ASYNC_WRAPPER { #parseError (err) { if (err) { - return {code: err.code, name: err.name, message: err.message}; + return { code: err.code, name: err.name, message: err.message }; } else { return null; diff --git a/src/main.js b/src/main.js index c97dd78..42abe4c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,6 @@ import express from "express"; import bodyParser from "body-parser"; import cookieParser from "cookie-parser"; -import cors from "cors"; import morgan from "morgan"; import LDAP from "./ldap.js"; @@ -27,18 +26,111 @@ const ldap = new LDAP(global.argv.ldapURL, global.config.basedn); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(cookieParser()); -app.use(cors({ origin: global.db.hostname })); app.use(morgan("combined")); -// endpoint handles both adding a new user and updating an existing user including password and groups -app.post("/users/:userid", (req, res) => {}); +app.listen(global.argv.listenPort, () => { + console.log(`proxmoxaas-api v${global.package.version} listening on port ${global.argv.listenPort}`); +}); -app.get("/users/:userid", (req, res) => {}); +/** + * GET - get API version + * responses: + * - 200: {version: string} + */ +app.get("/version", (req, res) => { + res.status(200).send({ version: global.package.version }); +}); -app.delete("/users/:userid", (req, res) => {}); +/** + * GET - echo request + * responses: + * - 200: {body: request.body, cookies: request.cookies} + */ +app.get("/echo", (req, res) => { + res.status(200).send({ body: req.body, cookies: req.cookies }); +}); + +/** + * POST - create a new user or modify existing user attributes + */ +app.post("/users/:userid", async (req, res) => { + const params = { + userid: req.params.userid, + bind: ldap.createUserBind(req.body.binduser, req.body.bindpass), + userattrs: { + cn: req.body.usercn, + sn: req.body.usersn, + userPassword: req.body.userpassword + } + }; + const checkUser = await ldap.getUser(params.bind, params.userid); + 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); + res.send({ + ok: result.ok, + error: result.error + }); + } + else if (checkUser.ok) { // the user does exist, modify the user entries + const result = await ldap.modUser(params.bind, params.userid, params.userattrs); + res.send({ + ok: result.ok, + error: result.error + }); + } + else { // some other error happened + res.send({ + ok: checkUser.ok, + error: checkUser.error + }); + } +}); + +/** + * GET - get user attributes + */ +app.get("/users/:userid", async (req, res) => { + const params = { + userid: req.params.userid, + bind: ldap.createUserBind(req.body.binduser, req.body.bindpass) + }; + const result = await ldap.getUser(params.bind, params.userid); + if (result.ok) { + res.send({ + ok: result.ok, + error: result.error, + user: result.user + }); + } + else { + res.send({ + ok: result.ok, + error: result.error + }); + } +}); + +/** + * DELETE - delete user + */ +app.delete("/users/:userid", async (req, res) => { + const params = { + userid: req.params.userid, + bind: ldap.createUserBind(req.body.binduser, req.body.bindpass) + }; + const result = await ldap.delUser(params.bind, params.userid); + res.send({ + ok: result.ok, + error: result.error + }); +}); app.post("/groups/:groupid", (req, res) => {}); app.get("/groups/:groupid", (req, res) => {}); -app.delete("/groups/:groupid", (req, res) => {}); \ No newline at end of file +app.delete("/groups/:groupid", (req, res) => {}); + +app.get("/groups/:groupid/members", (req, res) => {}); + +app.post("/groups/:groupid/members", (req, res) => {});