managed endpoint, some error handling changes
This commit is contained in:
parent
6724e8724a
commit
fbadd95c4f
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
/node_modules/
|
/node_modules/
|
||||||
/keys.json
|
/keys.json
|
||||||
|
/managed.json
|
||||||
*.zone
|
*.zone
|
||||||
/dist
|
/dist
|
||||||
/logs
|
/logs
|
||||||
|
1015
package-lock.json
generated
1015
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,12 +13,14 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcrypt": "^5.0.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-async-errors": "^3.1.1",
|
"express-async-errors": "^3.1.1",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/bcrypt": "^5.0.0",
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/node": "^16.7.10",
|
"@types/node": "^16.7.10",
|
||||||
|
@ -89,9 +89,7 @@ export class DNSCache {
|
|||||||
await this.validator.validateAndSave(name, zone);
|
await this.validator.validateAndSave(name, zone);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// Reload previous state
|
// Reload previous state
|
||||||
if (e.message.contains('Validation')) {
|
|
||||||
await this.load(name, zone.file);
|
await this.load(name, zone.file);
|
||||||
}
|
|
||||||
throw e as Error;
|
throw e as Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
src/index.ts
38
src/index.ts
@ -18,6 +18,7 @@ import { fromRequest } from './ip/from-request';
|
|||||||
import { Keys } from './keys';
|
import { Keys } from './keys';
|
||||||
import { CachedZone } from './models/interfaces';
|
import { CachedZone } from './models/interfaces';
|
||||||
import { logger } from './log/Logger';
|
import { logger } from './log/Logger';
|
||||||
|
import { Managed } from './managed';
|
||||||
|
|
||||||
const port = parseInt(process.env.PORT || '9129', 10);
|
const port = parseInt(process.env.PORT || '9129', 10);
|
||||||
const cacheTTL = parseInt(process.env.CACHE_TTL || '2629746', 10);
|
const cacheTTL = parseInt(process.env.CACHE_TTL || '2629746', 10);
|
||||||
@ -35,6 +36,7 @@ app.use(cors({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const keys = new Keys();
|
const keys = new Keys();
|
||||||
|
const managed = new Managed();
|
||||||
const rndc = ReloadExecutor.fromEnvironment();
|
const rndc = ReloadExecutor.fromEnvironment();
|
||||||
const validator = new ValidatorExecutor();
|
const validator = new ValidatorExecutor();
|
||||||
const cache = new DNSCache(rndc, validator, cacheTTL);
|
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'));
|
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) => {
|
const errorHandler: ErrorRequestHandler = (err: any, _req: Request, res: Response, _next: NextFunction) => {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
success: false,
|
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);
|
app.use('/api/v1', api);
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
await keys.load();
|
await keys.load();
|
||||||
|
await managed.load();
|
||||||
|
|
||||||
if (logger.logToFile) {
|
if (logger.logToFile) {
|
||||||
try {
|
try {
|
||||||
|
32
src/managed.ts
Normal file
32
src/managed.ts
Normal 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
10
src/models/managed.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface UserAccess {
|
||||||
|
token: string;
|
||||||
|
zone: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
name: string;
|
||||||
|
password: string;
|
||||||
|
access: UserAccess[];
|
||||||
|
}
|
Reference in New Issue
Block a user