improve backend handler design
This commit is contained in:
parent
072b5ef2d4
commit
4984877ab7
@ -54,17 +54,39 @@ class BACKEND {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AtomicChange {
|
||||||
|
constructor (valid, delta, callback, status = { ok: true, status: 200, message: "" }) {
|
||||||
|
this.valid = valid;
|
||||||
|
this.delta = delta;
|
||||||
|
this.callback = callback;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the change using the saved delta using the callback function
|
||||||
|
*/
|
||||||
|
async commit () {
|
||||||
|
const res = await this.callback(this.delta);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doNothingCallback (delta) {
|
||||||
|
return { ok: true, status: 200, message: "" };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for backend types that store/interact with user & group data.
|
* Interface for backend types that store/interact with user & group data.
|
||||||
* Not all backends need to implement all interface methods.
|
* Not all backends need to implement all interface methods.
|
||||||
*/
|
*/
|
||||||
class USER_BACKEND extends BACKEND {
|
class USER_BACKEND extends BACKEND {
|
||||||
/**
|
/**
|
||||||
* Add user to backend
|
* Validate an add user operation with the following parameters.
|
||||||
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
* @param {{id: string, realm: string}} user
|
* @param {{id: string, realm: string}} user
|
||||||
* @param {Object} attributes user attributes
|
* @param {Object} attributes user attributes
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
addUser (user, attributes, params) {}
|
addUser (user, attributes, params) {}
|
||||||
|
|
||||||
@ -84,28 +106,31 @@ class USER_BACKEND extends BACKEND {
|
|||||||
getAllUsers (params) {}
|
getAllUsers (params) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify user in backend
|
* Validate a set user operation with the following parameters.
|
||||||
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
* @param {{id: string, realm: string}} user
|
* @param {{id: string, realm: string}} user
|
||||||
* @param {Object} attributes new user attributes to modify
|
* @param {Object} attributes new user attributes to modify
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
setUser (user, attributes, params) {}
|
setUser (user, attributes, params) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete user from backend
|
* Validate a delete user operation with the following parameters.
|
||||||
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
* @param {{id: string, realm: string}} user
|
* @param {{id: string, realm: string}} user
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
delUser (user, params) {}
|
delUser (user, params) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add group to backend
|
* Validate an add group operation with the following parameters.
|
||||||
* @param {{id: string}} group
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
|
* @param {{id: string, realm: string}} group
|
||||||
* @param {Object} attributes group attributes
|
* @param {Object} attributes group attributes
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
addGroup (group, attributes, params) {}
|
addGroup (group, attributes, params) {}
|
||||||
|
|
||||||
@ -125,37 +150,40 @@ class USER_BACKEND extends BACKEND {
|
|||||||
getAllGroups (params) {}
|
getAllGroups (params) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify group in backend
|
* Validate a set group operation with the following parameters.
|
||||||
* @param {{id: string}} group
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
* @param {Object} attributes new group attributes to modify
|
* @param {{id: string, realm: string}} group
|
||||||
|
* @param {Object} attributes group attributes
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
setGroup (group, attributes, params) {}
|
setGroup (group, attributes, params) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete group from backend
|
* Validate a del group operation with the following parameters.
|
||||||
* @param {{id: string}} group
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
|
* @param {{id: string, realm: string}} group
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
delGroup (group, params) {}
|
delGroup (group, attributes, params) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add user to group
|
* Validate an add user to group operation with the following parameters.
|
||||||
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
* @param {{id: string, realm: string}} user
|
* @param {{id: string, realm: string}} user
|
||||||
* @param {{id: string}} group
|
* @param {{id: string}} group
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
addUserToGroup (user, group, params) {}
|
addUserToGroup (user, group, params) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove user from group
|
* Validate a remove user from group operation with the following parameters.
|
||||||
|
* Returns whether the change is valid and a delta object to be used in the operation.
|
||||||
* @param {{id: string, realm: string}} user
|
* @param {{id: string, realm: string}} user
|
||||||
* @param {{id: string}} group
|
* @param {{id: string}} group
|
||||||
* @param {Object} params authentication params, usually req.cookies
|
* @param {Object} params authentication params, usually req.cookies
|
||||||
* @returns {{ok: boolean, status: number, message: string}} error object or null
|
* @returns {AtomicChange} atomic change object
|
||||||
*/
|
*/
|
||||||
delUserFromGroup (user, group, params) {}
|
delUserFromGroup (user, group, params) {}
|
||||||
}
|
}
|
||||||
@ -218,20 +246,30 @@ class USER_BACKEND_MANAGER extends USER_BACKEND {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setUser (user, attributes, params) {
|
async setUser (user, attributes, params) {
|
||||||
const results = {
|
const atomicChanges = [];
|
||||||
|
for (const backend of this.#config.realm[user.realm]) {
|
||||||
|
const atomicChange = await global.backends[backend].setUser(user, attributes, params);
|
||||||
|
if (atomicChange.valid === false) { // if any fails, preemptively exit
|
||||||
|
return atomicChange.stauts;
|
||||||
|
}
|
||||||
|
atomicChanges.push(atomicChange); // queue callback into array
|
||||||
|
}
|
||||||
|
const response = {
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
message: ""
|
message: "",
|
||||||
|
allResponses: []
|
||||||
};
|
};
|
||||||
for (const backend of this.#config.realm[user.realm]) {
|
for (const atomicChange of atomicChanges) {
|
||||||
const result = await global.backends[backend].setUser(user, attributes, params);
|
const atomicResponse = await atomicChange.commit();
|
||||||
if (!result) {
|
if (atomicResponse.ok === false) {
|
||||||
results.ok = false;
|
response.ok = false;
|
||||||
results.status = 500;
|
response.status = atomicResponse.status;
|
||||||
return results;
|
response.message = atomicResponse.message;
|
||||||
}
|
}
|
||||||
|
response.allResponses.push(); // execute callback
|
||||||
}
|
}
|
||||||
return results;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
delUser (user, params) {}
|
delUser (user, params) {}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { readFileSync, writeFileSync } from "fs";
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
import { exit } from "process";
|
import { exit } from "process";
|
||||||
import { DB_BACKEND } from "./backends.js";
|
import { AtomicChange, DB_BACKEND, doNothingCallback } from "./backends.js";
|
||||||
|
|
||||||
export default class LocalDB extends DB_BACKEND {
|
export default class LocalDB extends DB_BACKEND {
|
||||||
#path = null;
|
#path = null;
|
||||||
@ -35,22 +35,7 @@ export default class LocalDB extends DB_BACKEND {
|
|||||||
writeFileSync(this.#path, JSON.stringify(this.#data));
|
writeFileSync(this.#path, JSON.stringify(this.#data));
|
||||||
}
|
}
|
||||||
|
|
||||||
addUser (user, attributes, params) {
|
addUser (user, attributes, params) {}
|
||||||
const username = `${user.id}@${user.realm}`;
|
|
||||||
if (this.#data.users[username]) { // user already exists
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
status: 1,
|
|
||||||
message: "User already exists"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
attributes = attributes || this.#defaultuser;
|
|
||||||
this.#data.users[username] = attributes;
|
|
||||||
this.#save();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getUser (user, params) {
|
getUser (user, params) {
|
||||||
const requestedUser = `${user.id}@${user.realm}`;
|
const requestedUser = `${user.id}@${user.realm}`;
|
||||||
@ -76,33 +61,42 @@ export default class LocalDB extends DB_BACKEND {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setUser (user, attributes, params) {
|
setUser (user, attributes, params) {
|
||||||
if (attributes.resources && attributes.cluster && attributes.templates) { // localdb should only deal with these attributes
|
if (attributes.resources && attributes.cluster && attributes.templates) {
|
||||||
const username = `${user.id}@${user.realm}`;
|
const username = `${user.id}@${user.realm}`;
|
||||||
if (this.#data.users[username]) {
|
if (this.#data.users[username]) {
|
||||||
this.#data.users[username] = attributes;
|
if (this.#data.users[params.username] && this.#data.users[params.username].cluster.admin) {
|
||||||
|
return new AtomicChange(false,
|
||||||
|
{
|
||||||
|
username,
|
||||||
|
attributes: {
|
||||||
|
resources: attributes.resources,
|
||||||
|
cluster: attributes.cluster,
|
||||||
|
templates: attributes.templates
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(delta) => {
|
||||||
|
this.#data.users[delta.username] = delta.attributes;
|
||||||
this.#save();
|
this.#save();
|
||||||
return true;
|
return { ok: true, status: 200, message: "" };
|
||||||
|
},
|
||||||
|
{ ok: true, status: 200, message: "" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return new AtomicChange(false, {}, doNothingCallback, { ok: false, status: 401, message: `${params.username} is not an admin user in localdb` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // if request is not setting these attributes, then assume its fine but do nothing
|
else {
|
||||||
return true;
|
// return false;
|
||||||
|
return new AtomicChange(false, {}, doNothingCallback, { ok: false, status: 400, message: `${username} was not found in localdb` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new AtomicChange(true, {}, doNothingCallback, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delUser (user, params) {
|
delUser (user, params) {}
|
||||||
const username = `${user.id}@${user.realm}`;
|
|
||||||
if (this.#data.users[username]) {
|
|
||||||
delete this.#data.users[username];
|
|
||||||
this.#save();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// group methods not implemented because db backend does not store groups
|
// group methods not implemented because db backend does not store groups
|
||||||
addGroup (group, atrributes, params) {}
|
addGroup (group, atrributes, params) {}
|
||||||
@ -115,26 +109,8 @@ export default class LocalDB extends DB_BACKEND {
|
|||||||
delGroup (group, params) {}
|
delGroup (group, params) {}
|
||||||
|
|
||||||
// assume that adding to group also adds to group's pool
|
// assume that adding to group also adds to group's pool
|
||||||
addUserToGroup (user, group, params) {
|
addUserToGroup (user, group, params) {}
|
||||||
const username = `${user.id}@${user.realm}`;
|
|
||||||
if (this.#data.users[username]) {
|
|
||||||
this.#data.users[username].cluster.pools[group.id] = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assume that adding to group also adds to group's pool
|
// assume that adding to group also adds to group's pool
|
||||||
delUserFromGroup (user, group, params) {
|
delUserFromGroup (user, group, params) {}
|
||||||
const username = `${user.id}@${user.realm}`;
|
|
||||||
if (this.#data.users[username] && this.#data.users[username].cluster.pools[group.id]) {
|
|
||||||
delete this.#data.users[username].cluster.pools[group.id];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { AUTH_BACKEND } from "./backends.js";
|
import { AtomicChange, AUTH_BACKEND, doNothingCallback } from "./backends.js";
|
||||||
import * as setCookie from "set-cookie-parser";
|
import * as setCookie from "set-cookie-parser";
|
||||||
|
|
||||||
export default class PAASLDAP extends AUTH_BACKEND {
|
export default class PAASLDAP extends AUTH_BACKEND {
|
||||||
@ -48,17 +48,12 @@ export default class PAASLDAP extends AUTH_BACKEND {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#handleGenericReturn (res) {
|
#handleGenericReturn (res) {
|
||||||
if (res.ok) { // if ok, return null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else { // if not ok, return error obj
|
|
||||||
return {
|
return {
|
||||||
ok: res.ok,
|
ok: res.ok,
|
||||||
status: res.status,
|
status: res.status,
|
||||||
message: res.ok ? "" : res.data.error
|
message: res.ok ? "" : res.data.error
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async openSession (user, password) {
|
async openSession (user, password) {
|
||||||
const username = user.id;
|
const username = user.id;
|
||||||
@ -86,10 +81,7 @@ export default class PAASLDAP extends AUTH_BACKEND {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addUser (user, attributes, params) {
|
async addUser (user, attributes, params) {}
|
||||||
const res = await this.#request(`/users/${user.id}`, "POST", params, attributes);
|
|
||||||
return this.#handleGenericReturn(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUser (user, params) {
|
async getUser (user, params) {
|
||||||
if (!params) { // params required, do nothing if params are missing
|
if (!params) { // params required, do nothing if params are missing
|
||||||
@ -124,19 +116,37 @@ export default class PAASLDAP extends AUTH_BACKEND {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setUser (user, attributes, params) {
|
async setUser (user, attributes, params) {
|
||||||
const res = await this.#request(`/users/${user.id}`, "POST", params, attributes);
|
if (!attributes.userpassword && !attributes.cn && attributes.sn) {
|
||||||
|
return new AtomicChange(true, {}, doNothingCallback, null); // change has no ldap attributes
|
||||||
|
}
|
||||||
|
const ldapAttributes = {};
|
||||||
|
if (attributes.userpassword) {
|
||||||
|
ldapAttributes.userpassword = attributes.userpassword;
|
||||||
|
}
|
||||||
|
if (attributes.cn) {
|
||||||
|
ldapAttributes.cn = attributes.cn;
|
||||||
|
}
|
||||||
|
if (attributes.sn) {
|
||||||
|
ldapAttributes.sn = attributes.sn;
|
||||||
|
}
|
||||||
|
return new AtomicChange(
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
user,
|
||||||
|
ldapAttributes,
|
||||||
|
params
|
||||||
|
},
|
||||||
|
async (delta) => {
|
||||||
|
const res = await this.#request(`/users/${delta.user.id}`, "POST", delta.params, delta.ldapAttributes);
|
||||||
return this.#handleGenericReturn(res);
|
return this.#handleGenericReturn(res);
|
||||||
|
},
|
||||||
|
{ ok: true, status: 200, message: "" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delUser (user, params) {
|
async delUser (user, params) {}
|
||||||
const res = await this.#request(`/users/${user.id}`, "DELETE", params);
|
|
||||||
return this.#handleGenericReturn(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async addGroup (group, attributes, params) {
|
async addGroup (group, attributes, params) {}
|
||||||
const res = await this.#request(`/groups/${group.id}`, "POST", params);
|
|
||||||
return this.#handleGenericReturn(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getGroup (group, params) {
|
async getGroup (group, params) {
|
||||||
return await this.#request(`/groups/${group.id}`, "GET", params);
|
return await this.#request(`/groups/${group.id}`, "GET", params);
|
||||||
@ -163,21 +173,12 @@ export default class PAASLDAP extends AUTH_BACKEND {
|
|||||||
|
|
||||||
async setGroup (group, attributes, params) {
|
async setGroup (group, attributes, params) {
|
||||||
// not implemented, LDAP groups do not have any attributes to change
|
// not implemented, LDAP groups do not have any attributes to change
|
||||||
return null;
|
return new AtomicChange(true, {}, doNothingCallback, null); ;
|
||||||
}
|
}
|
||||||
|
|
||||||
async delGroup (group, params) {
|
async delGroup (group, params) {}
|
||||||
const res = await this.#request(`/groups/${group.id}`, "DELETE", params);
|
|
||||||
return this.#handleGenericReturn(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async addUserToGroup (user, group, params) {
|
async addUserToGroup (user, group, params) {}
|
||||||
const res = await this.#request(`/groups/${group.id}/members/${user.id}`, "POST", params);
|
|
||||||
return this.#handleGenericReturn(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async delUserFromGroup (user, group, params) {
|
async delUserFromGroup (user, group, params) {}
|
||||||
const res = await this.#request(`/groups/${group.id}/members/${user.id}`, "DELETE", params);
|
|
||||||
return this.#handleGenericReturn(res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user