improve ldap return values,

fix addUserToGroup and delUser logic with user-group interaction
This commit is contained in:
Arthur Lu 2023-11-30 04:28:49 +00:00
parent b4ee79589b
commit 3ce798aced
3 changed files with 127 additions and 53 deletions

View File

@ -5,6 +5,7 @@ delete: olcAccess
- -
add: olcAccess add: olcAccess
olcAccess: {0}to attrs=userPassword olcAccess: {0}to attrs=userPassword
by group/groupOfNames/member="cn=admins,ou=groups,$BASE_DN" write
by self write by self write
by anonymous auth by anonymous auth
by * none by * none

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" -n > paas.token echo -n "$PAAS_PASSWD" > 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

View File

@ -17,9 +17,9 @@ export default class LDAP {
} }
async addUser (bind, uid, attrs) { async addUser (bind, uid, attrs) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const userDN = `uid=${uid},${this.#peopledn}`; const userDN = `uid=${uid},${this.#peopledn}`;
const entry = { const entry = {
@ -29,27 +29,24 @@ export default class LDAP {
uid, uid,
userPassword: attrs.userPassword userPassword: attrs.userPassword
}; };
return await this.#client.add(userDN, entry); const addResult = await this.#client.add(userDN, entry);
return { op: `add ${uid}`, ok: addResult.ok, error: addResult.error };
} }
async getUser (bind, uid) { async getUser (bind, uid) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const opts = { return await this.#client.search(`uid=${uid},${this.#peopledn}`, {});
filter: `(uid=${uid})`,
scope: "sub"
};
return await this.#client.search(this.#peopledn, opts);
} }
async modUser (bind, uid, newAttrs) { async modUser (bind, uid, newAttrs) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const results = []; const subops = [bindResult];
for (const attr of ["cn", "sn", "userPassword"]) { for (const attr of ["cn", "sn", "userPassword"]) {
if (attr in newAttrs) { if (attr in newAttrs) {
const change = new ldap.Change({ const change = new ldap.Change({
@ -59,25 +56,57 @@ export default class LDAP {
values: [newAttrs[attr]] values: [newAttrs[attr]]
} }
}); });
results.push(await this.#client.modify(`uid=${uid},${this.#peopledn}`, change)); subops.push(await this.#client.modify(`uid=${uid},${this.#peopledn}`, change));
} }
} }
return results; return { op: `modify ${uid}`, ok: !subops.some((e) => !e.ok), error: subops.find((e) => !e.ok) || null, subops };
} }
async delUser (bind, uid) { async delUser (bind, uid) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const userDN = `uid=${uid},${this.#peopledn}`; const userDN = `uid=${uid},${this.#peopledn}`;
return await this.#client.del(userDN); const delResult = await this.#client.del(userDN);
const groups = await this.#client.search(this.#groupsdn, {
scope: "one",
filter: `(member=uid=${uid},${this.#peopledn})`
});
if (!groups.ok) {
return { op: `del ${uid}`, ok: groups.ok, error: groups.error, subops: [bindResult, delResult, groups]}
}
const groupsubops = [];
for (const element of groups.entries) {
let change = null;
if (element.attributes.member.length === 1) {
change = new ldap.Change({
operation: "replace",
modification: {
type: "member",
values: [""]
}
});
}
else {
change = new ldap.Change({
operation: "delete",
modification: {
type: "member",
values: [`uid=${uid},${this.#peopledn}`]
}
});
}
const delResult = await this.#client.modify(element.dn, change);
groupsubops.push(delResult);
}
return { op: `del ${uid}`, ok: delResult.ok, error: delResult.error, subops: [bindResult, delResult, groups].concat(groupsubops) };
} }
async addGroup (bind, gid, attrs) { async addGroup (bind, gid, attrs) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const groupDN = `cn=${gid},${this.#groupsdn}`; const groupDN = `cn=${gid},${this.#groupsdn}`;
const entry = { const entry = {
@ -85,37 +114,72 @@ export default class LDAP {
member: attrs && attrs.member ? attrs.member : "", member: attrs && attrs.member ? attrs.member : "",
cn: gid cn: gid
}; };
return await this.#client.add(groupDN, entry); const addResult = await this.#client.add(groupDN, entry);
return { op: `add ${gid}`, ok: addResult.ok, error: addResult.error, subops: [bindResult, addResult] };
}
async getGroup (bind, gid) {
const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!bindResult.ok) {
return bindResult;
}
return await this.#client.search(`cn=${gid},${this.#groupsdn}`, {});
} }
async delGroup (bind, gid) { async delGroup (bind, gid) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const groupDN = `cn=${gid},${this.#groupsdn}`; const groupDN = `cn=${gid},${this.#groupsdn}`;
return await this.#client.del(groupDN); const delResult = await this.#client.del(groupDN);
return { op: `del ${gid}`, ok: delResult.ok, error: delResult.error, subops: [bindResult, delResult] };
} }
async addUserToGroup (bind, uid, gid) { async addUserToGroup (bind, uid, gid) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const change = new ldap.Change({ const checkGroupEntry = await this.#client.search(`cn=${gid},${this.#groupsdn}`, {});
operation: "add", if (checkGroupEntry.ok) {
modification: { // add the user
type: "member", const change = new ldap.Change({
values: [`uid=${uid},${this.#peopledn}`] operation: "add",
modification: {
type: "member",
values: [`uid=${uid},${this.#peopledn}`]
}
});
const addResult = await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change);
if (!addResult.ok) {
return addResult;
} }
}); // check if there is a blank entry in the group
return await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change); const groupEntry = checkGroupEntry.entries[0];
if (groupEntry.attributes.member.includes("")) {
// delete the blank user
const change = new ldap.Change({
operation: "delete",
modification: {
type: "member",
values: [""]
}
});
const fixResult = await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change);
return { op: `add ${uid} to ${gid}`, ok: addResult.ok && fixResult.ok, error: addResult.error ? addResult.error : fixResult.error, subops: [bindResult, addResult, fixResult] };
}
return { op: `add ${uid} to ${gid}`, ok: true, error: null, subops: [bindResult, addResult] };
}
else {
return { op: `add ${uid} to ${gid}`, ok: false, error: `${gid} does not exist`, subops: [bindResult] };
}
} }
async delUserFromGroup (bind, uid, gid) { async delUserFromGroup (bind, uid, gid) {
const result = await this.#client.bind(bind.dn, bind.password); const bindResult = await this.#client.bind(bind.dn, bind.password);
if (!result.ok) { if (!bindResult.ok) {
return result; return bindResult;
} }
const change = new ldap.Change({ const change = new ldap.Change({
operation: "delete", operation: "delete",
@ -124,7 +188,12 @@ export default class LDAP {
values: [`uid=${uid},${this.#peopledn}`] values: [`uid=${uid},${this.#peopledn}`]
} }
}); });
return await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change); const delResult = await this.#client.modify(`cn=${gid},${this.#groupsdn}`, change);
return { op: `del ${uid} from ${gid}`, ok: delResult.ok, error: delResult.error, subops: [bindResult, delResult] };
}
async search (base, opts) {
return await this.#client.search(base, opts);
} }
} }
@ -147,7 +216,7 @@ class LDAPJS_CLIENT_ASYNC_WRAPPER {
resolve({ op: `bind ${dn}`, ok: false, error: err }); resolve({ op: `bind ${dn}`, ok: false, error: err });
} }
else { else {
resolve({ op: `bind ${dn}`, ok: true }); resolve({ op: `bind ${dn}`, ok: true, error: null });
} }
}); });
}); });
@ -160,7 +229,7 @@ class LDAPJS_CLIENT_ASYNC_WRAPPER {
resolve({ op: `add ${dn}`, ok: false, error: err }); resolve({ op: `add ${dn}`, ok: false, error: err });
} }
else { else {
resolve({ op: `add ${dn}`, ok: true }); resolve({ op: `add ${dn}`, ok: true, error: null });
} }
}); });
}); });
@ -172,10 +241,14 @@ class LDAPJS_CLIENT_ASYNC_WRAPPER {
if (err) { if (err) {
return resolve({ op: `search ${base}`, ok: false, error: err }); return resolve({ op: `search ${base}`, ok: false, error: err });
} }
const results = { ok: false, status: 1, message: "", entries: [] }; const results = { op: `search ${base}`, ok: false, error: null, status: 1, message: "", entries: [] };
res.on("searchRequest", (searchRequest) => { }); res.on("searchRequest", (searchRequest) => { });
res.on("searchEntry", (entry) => { res.on("searchEntry", (entry) => {
results.entries.push({ dn: entry.pojo.objectName, attributes: entry.pojo.attributes }); const attributes = {};
for (const element of entry.pojo.attributes) {
attributes[element.type] = element.values;
}
results.entries.push({ dn: entry.pojo.objectName, attributes });
}); });
res.on("searchReference", (referral) => { }); res.on("searchReference", (referral) => { });
res.on("error", (error) => { res.on("error", (error) => {
@ -198,10 +271,10 @@ class LDAPJS_CLIENT_ASYNC_WRAPPER {
return new Promise((resolve) => { return new Promise((resolve) => {
this.#client.modify(name, changes, (err) => { this.#client.modify(name, changes, (err) => {
if (err) { if (err) {
resolve({ op: `modify ${name}`, ok: false, error: err }); resolve({ op: `modify ${name} ${changes.operation} ${changes.modification.type}`, ok: false, error: err });
} }
else { else {
resolve({ op: `modify ${name}`, ok: true }); resolve({ op: `modify ${name} ${changes.operation} ${changes.modification.type}`, ok: true, error: null });
} }
}); });
}); });
@ -214,7 +287,7 @@ class LDAPJS_CLIENT_ASYNC_WRAPPER {
resolve({ op: `del ${dn}`, ok: false, error: err }); resolve({ op: `del ${dn}`, ok: false, error: err });
} }
else { else {
resolve({ op: `del ${dn}`, ok: true }); resolve({ op: `del ${dn}`, ok: true, error: null });
} }
}); });
}); });