managed endpoint, some error handling changes

This commit is contained in:
Evert Prants 2022-02-26 10:40:40 +02:00
parent 6724e8724a
commit fbadd95c4f
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
7 changed files with 1098 additions and 4 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
/node_modules/
/keys.json
/managed.json
*.zone
/dist
/logs

1015
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,12 +13,14 @@
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.0.1",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-async-errors": "^3.1.1",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/node": "^16.7.10",

View File

@ -89,9 +89,7 @@ export class DNSCache {
await this.validator.validateAndSave(name, zone);
} catch (e: any) {
// Reload previous state
if (e.message.contains('Validation')) {
await this.load(name, zone.file);
}
throw e as Error;
}
}

View File

@ -18,6 +18,7 @@ import { fromRequest } from './ip/from-request';
import { Keys } from './keys';
import { CachedZone } from './models/interfaces';
import { logger } from './log/Logger';
import { Managed } from './managed';
const port = parseInt(process.env.PORT || '9129', 10);
const cacheTTL = parseInt(process.env.CACHE_TTL || '2629746', 10);
@ -35,6 +36,7 @@ app.use(cors({
}));
const keys = new Keys();
const managed = new Managed();
const rndc = ReloadExecutor.fromEnvironment();
const validator = new ValidatorExecutor();
const cache = new DNSCache(rndc, validator, cacheTTL);
@ -535,6 +537,39 @@ api.post('/set-ip/:domain', domainAuthorization, async (req, res) => {
logger.info('zone %s set-ip from %s: %s', domain, req.ip, actions.join('\n'));
});
const checkManaged: RequestHandler = (req, res, next) => {
if (!req.body.name || !req.body.password) {
return next(new Error('missing_fields'));
}
// Find user account
const findUser = managed.getAccount(req.body.name);
if (!findUser) {
return next(new Error('invalid'));
}
console.log(findUser);
// Validate password
managed.validatePassword(findUser, req.body.password).then((success) => {
if (success) {
res.locals.user = findUser;
return next();
}
next(new Error('invalid'));
});
}
/**
* Simple front-end access utility by username and password
*/
app.post('/api/v1/managed', checkManaged, (req, res) => {
res.json({
name: res.locals.user.name,
access: res.locals.user.access
});
});
const errorHandler: ErrorRequestHandler = (err: any, _req: Request, res: Response, _next: NextFunction) => {
res.status(400).json({
success: false,
@ -542,11 +577,12 @@ const errorHandler: ErrorRequestHandler = (err: any, _req: Request, res: Respons
});
}
api.use(errorHandler);
app.use(errorHandler);
app.use('/api/v1', api);
async function load() {
await keys.load();
await managed.load();
if (logger.logToFile) {
try {

32
src/managed.ts Normal file
View File

@ -0,0 +1,32 @@
import { compare } from 'bcrypt';
import * as fs from 'fs/promises';
import path from 'path';
import { User } from './models/managed';
export class Managed {
private accounts: User[] = [];
async load(): Promise<void> {
const file = path.join(__dirname, '..', 'managed.json');
let content = '[]';
try {
content = await fs.readFile(file, { encoding: 'utf-8' });
} catch (e: unknown) {
if ((e as Error).message.includes('ENOENT')) {
fs.writeFile(file, '[]');
} else {
throw e;
}
}
this.accounts = JSON.parse(content);
}
getAccount(name: string): User | undefined {
return this.accounts.find((item) => item.name === name);
}
async validatePassword(account: User, password: string): Promise<boolean> {
return compare(password, account.password);
}
}

10
src/models/managed.ts Normal file
View File

@ -0,0 +1,10 @@
export interface UserAccess {
token: string;
zone: string;
}
export interface User {
name: string;
password: string;
access: UserAccess[];
}