Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Added modifying meta/deleting users via API #189

Merged
merged 5 commits into from
Dec 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,81 @@ export const setUserPassword = (unid: string, password: string): Promise<User> =
.catch(reject);
});

/**
* Deletes a user account
* @since v0.14.1
*/
export const deleteUser = (unid: string): Promise<void> => new Promise((resolve, reject) => {

// Find the user
const user = users.find((user) => user.unid === unid);
if (!user) return reject(new Error('User not found'));

// Remove the user from the users map
users.splice(users.indexOf(user), 1);

// Save the new user to auth.json
const authPath = path('auth.json');
const authData = fs.readJsonSync(authPath) as Users;
const userIndex = authData.users.findIndex((user) => user.unid === unid);
authData.users.splice(userIndex, 1);
fs.writeJson(authPath, authData, { spaces: '\t' })
.then(() => resolve())
.catch(reject);
});

/**
* Sets a meta value for a user
* @since v0.14.1
*/
export const setUserMeta = (unid: string, key: string, value: any, force = false): Promise<User> => new Promise((resolve, reject) => {

// Find the user
const user = users.find((user) => user.unid === unid);
if (!user) return reject(new Error('User not found'));

// Set the meta value
if (user.meta[key] && !force) return reject(new Error('Meta key already exists'));

user.meta[key] = value;

// Save the new user to auth.json
const authPath = path('auth.json');
const authData = fs.readJsonSync(authPath) as Users;
const userIndex = authData.users.findIndex((user) => user.unid === unid);
authData.users[userIndex] = user;
fs.writeJson(authPath, authData, { spaces: '\t' })
.then(() => log.info('Set meta value for', user.unid, `${key}=${value}`))
.then(() => resolve(user))
.catch(reject);
});

/**
* Deletes a meta value for a user
* @since v0.14.1
*/
export const deleteUserMeta = (unid: string, key: string): Promise<User> => new Promise((resolve, reject) => {

// Find the user
const user = users.find((user) => user.unid === unid);
if (!user) return reject(new Error('User not found'));

// Delete the meta value
if (!user.meta[key]) return reject(new Error('Meta key does not exist'));

delete user.meta[key];

// Save the new user to auth.json
const authPath = path('auth.json');
const authData = fs.readJsonSync(authPath) as Users;
const userIndex = authData.users.findIndex((user) => user.unid === unid);
authData.users[userIndex] = user;
fs.writeJson(authPath, authData, { spaces: '\t' })
.then(() => log.info('Deleted meta value for', user.unid, key))
.then(() => resolve(user))
.catch(reject);
});

/**
* Called by ass.ts on startup
* @since v0.14.0
Expand Down
63 changes: 60 additions & 3 deletions src/routers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { Router, Request, Response, NextFunction } from 'express';
import { findFromToken, setUserPassword, users, createNewUser, verifyCliKey } from '../auth';
import { findFromToken, setUserPassword, users, createNewUser, deleteUser, setUserMeta, deleteUserMeta, verifyCliKey } from '../auth';
import { log } from '../utils';
import { data } from '../data';
import { User } from '../types/auth';
Expand All @@ -15,6 +15,19 @@ import { User } from '../types/auth';
*/
const RouterApi = Router();

/**
* Logs an error and sends a 500 (404 if 'User not found' error)
* @since v0.14.1
*/
const errorHandler = (res: Response, err: Error | any) => {
log.error(err);
switch (err.message) {
case 'User not found': return res.sendStatus(404);
case 'Meta key already exists': return res.sendStatus(409);
default: return res.sendStatus(500);
}
};

/**
* Token authentication middleware for Admins
* @since v0.14.0
Expand Down Expand Up @@ -56,7 +69,7 @@ function buildUserRouter() {

setUserPassword(id, newPassword)
.then(() => res.sendStatus(200))
.catch((err) => (log.error(err), res.sendStatus(500)));
.catch((err) => errorHandler(res, err));
});

// Create a new user
Expand All @@ -73,14 +86,58 @@ function buildUserRouter() {

createNewUser(username, password, admin, meta)
.then((user) => res.send(user))
.catch((err) => (log.error(err), res.sendStatus(500)));
.catch((err) => errorHandler(res, err));
});

// Get all users
// Admin only
userRouter.get('/all', adminAuthMiddleware, (req: Request, res: Response) => res.json(users));

// Get a user (must be last as it's a catch-all)
// Admin only
userRouter.get('/:id', adminAuthMiddleware, (req: Request, res: Response) =>
userFinder(res, users.find(user => user.unid === req.params.id || user.username === req.params.id)));

// Delete a user
// Admin only
userRouter.delete('/:id', adminAuthMiddleware, (req: Request, res: Response) => {
const id = req.params.id;

deleteUser(id)
.then(() => res.sendStatus(200))
.catch((err) => errorHandler(res, err));
});

// Update a user meta key/value (/meta can be after /:id because they are not HTTP GET)
// Admin only
userRouter.put('/meta/:id', adminAuthMiddleware, (req: Request, res: Response) => {
const id = req.params.id;
const key: string | undefined = req.body.key;
const value: any = req.body.value;
const force = req.body.force ?? false;

if (key == null || key.length === 0 || value == null || value.length === 0)
return res.sendStatus(400);

setUserMeta(id, key, value, force)
.then(() => res.sendStatus(200))
.catch((err) => errorHandler(res, err));
});

// Delete a user meta key
// Admin only
userRouter.delete('/meta/:id', adminAuthMiddleware, (req: Request, res: Response) => {
const id = req.params.id;
const key: string | undefined = req.body.key;

if (key == null || key.length === 0)
return res.sendStatus(400);

deleteUserMeta(id, key)
.then(() => res.sendStatus(200))
.catch((err) => errorHandler(res, err));
});

return userRouter;
}

Expand Down