feat(ops-auth): add scoped API token auth across ops endpoints
This commit is contained in:
@@ -6,6 +6,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- add scoped API token auth across ops endpoints (ops-auth)
|
||||||
|
- introduces a shared requireOpsAuth helper that validates JWT identities and API tokens with scope and admin-policy checks
|
||||||
|
- applies explicit per-endpoint authorization across config, logs, stats, security, VPN, RADIUS, remote ingress, users, API tokens, and related ops handlers
|
||||||
|
- extends request interfaces and UI scope definitions to support apiToken-based access and adds tests for auth behavior and migration bridging
|
||||||
|
|
||||||
## 2026-05-19 - 13.31.0
|
## 2026-05-19 - 13.31.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@
|
|||||||
"@push.rocks/smartjwt": "^2.2.2",
|
"@push.rocks/smartjwt": "^2.2.2",
|
||||||
"@push.rocks/smartlog": "^3.2.2",
|
"@push.rocks/smartlog": "^3.2.2",
|
||||||
"@push.rocks/smartmetrics": "^3.0.3",
|
"@push.rocks/smartmetrics": "^3.0.3",
|
||||||
"@push.rocks/smartmigration": "1.3.1",
|
"@push.rocks/smartmigration": "1.4.1",
|
||||||
"@push.rocks/smartmta": "^5.3.3",
|
"@push.rocks/smartmta": "^5.3.3",
|
||||||
"@push.rocks/smartnetwork": "^4.7.1",
|
"@push.rocks/smartnetwork": "^4.7.1",
|
||||||
"@push.rocks/smartpath": "^6.0.0",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
|
|||||||
Generated
+5
-5
@@ -69,8 +69,8 @@ importers:
|
|||||||
specifier: ^3.0.3
|
specifier: ^3.0.3
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
'@push.rocks/smartmigration':
|
'@push.rocks/smartmigration':
|
||||||
specifier: 1.3.1
|
specifier: 1.4.1
|
||||||
version: 1.3.1(@push.rocks/smartbucket@4.6.1)(@push.rocks/smartdata@7.1.7(socks@2.8.8))
|
version: 1.4.1(@push.rocks/smartbucket@4.6.1)(@push.rocks/smartdata@7.1.7(socks@2.8.8))
|
||||||
'@push.rocks/smartmta':
|
'@push.rocks/smartmta':
|
||||||
specifier: ^5.3.3
|
specifier: ^5.3.3
|
||||||
version: 5.3.3
|
version: 5.3.3
|
||||||
@@ -1366,8 +1366,8 @@ packages:
|
|||||||
'@push.rocks/smartmetrics@3.0.3':
|
'@push.rocks/smartmetrics@3.0.3':
|
||||||
resolution: {integrity: sha512-RYY4NOla3kraZYVF9TBHgIz4/hSkqVDVNP7tLwhLK5mGBPBy8I/9NWXX6txZKQw6QihP85YD8mWUuUu2xS4D6Q==}
|
resolution: {integrity: sha512-RYY4NOla3kraZYVF9TBHgIz4/hSkqVDVNP7tLwhLK5mGBPBy8I/9NWXX6txZKQw6QihP85YD8mWUuUu2xS4D6Q==}
|
||||||
|
|
||||||
'@push.rocks/smartmigration@1.3.1':
|
'@push.rocks/smartmigration@1.4.1':
|
||||||
resolution: {integrity: sha512-qU3vc4yCLn8vJQIEMQwS2Lq6Ra8ixSfjutnbR1L/hauCzFRCic3o/DnFKB7pjj5jWaqSDG5nlyeIliLmC5aGsg==}
|
resolution: {integrity: sha512-kBvWuqBIIgkK2QskjHl0/MPLXYu4CDJDyuPc1KBDPBNejYIJp6hOZtbsmj4DYoNKsgFTpAALJn9JmUEdLe9E4g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@push.rocks/smartbucket': ^4.6.1
|
'@push.rocks/smartbucket': ^4.6.1
|
||||||
'@push.rocks/smartdata': ^7.1.7
|
'@push.rocks/smartdata': ^7.1.7
|
||||||
@@ -6508,7 +6508,7 @@ snapshots:
|
|||||||
'@push.rocks/smartdelay': 3.1.0
|
'@push.rocks/smartdelay': 3.1.0
|
||||||
'@push.rocks/smartlog': 3.2.2
|
'@push.rocks/smartlog': 3.2.2
|
||||||
|
|
||||||
'@push.rocks/smartmigration@1.3.1(@push.rocks/smartbucket@4.6.1)(@push.rocks/smartdata@7.1.7(socks@2.8.8))':
|
'@push.rocks/smartmigration@1.4.1(@push.rocks/smartbucket@4.6.1)(@push.rocks/smartdata@7.1.7(socks@2.8.8))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartlog': 3.2.2
|
'@push.rocks/smartlog': 3.2.2
|
||||||
'@push.rocks/smartversion': 3.1.0
|
'@push.rocks/smartversion': 3.1.0
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const setupHandler = (scopes: TScope[], options?: {
|
|||||||
const opsServerRef: any = {
|
const opsServerRef: any = {
|
||||||
typedrouter,
|
typedrouter,
|
||||||
adminHandler: {
|
adminHandler: {
|
||||||
|
validateIdentity: async () => null,
|
||||||
adminIdentityGuard: {
|
adminIdentityGuard: {
|
||||||
exec: async () => false,
|
exec: async () => false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
|
import { ConfigHandler } from '../ts/opsserver/handlers/config.handler.js';
|
||||||
|
import * as plugins from '../ts/plugins.js';
|
||||||
|
import * as interfaces from '../ts_interfaces/index.js';
|
||||||
|
|
||||||
|
const fireTypedRequest = async (
|
||||||
|
router: plugins.typedrequest.TypedRouter,
|
||||||
|
method: string,
|
||||||
|
request: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
return await router.routeAndAddResponse({
|
||||||
|
method,
|
||||||
|
request,
|
||||||
|
response: {},
|
||||||
|
correlation: {
|
||||||
|
id: `${method}-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
||||||
|
phase: 'request',
|
||||||
|
},
|
||||||
|
} as any, { localRequest: true, skipHooks: true }) as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeOpsServer = (scopes: interfaces.data.TApiTokenScope[]) => {
|
||||||
|
const router = new plugins.typedrequest.TypedRouter();
|
||||||
|
const token = {
|
||||||
|
id: 'token-1',
|
||||||
|
name: 'config-token',
|
||||||
|
tokenHash: 'hash',
|
||||||
|
scopes,
|
||||||
|
createdBy: 'token-user',
|
||||||
|
createdAt: Date.now(),
|
||||||
|
expiresAt: null,
|
||||||
|
lastUsedAt: null,
|
||||||
|
enabled: true,
|
||||||
|
} as interfaces.data.IStoredApiToken;
|
||||||
|
|
||||||
|
const opsServerRef = {
|
||||||
|
viewRouter: router,
|
||||||
|
adminHandler: {
|
||||||
|
validateIdentity: async () => null,
|
||||||
|
},
|
||||||
|
dcRouterRef: {
|
||||||
|
options: {
|
||||||
|
dbConfig: { enabled: false },
|
||||||
|
},
|
||||||
|
resolvedPaths: {
|
||||||
|
dcrouterHomeDir: '/tmp/dcrouter-home',
|
||||||
|
dataDir: '/tmp/dcrouter-data',
|
||||||
|
defaultTsmDbPath: '/tmp/dcrouter-data/db',
|
||||||
|
},
|
||||||
|
detectedPublicIp: null,
|
||||||
|
apiTokenManager: {
|
||||||
|
validateToken: async (rawTokenArg: string) => rawTokenArg === 'valid-token' ? token : null,
|
||||||
|
hasScope: (storedTokenArg: interfaces.data.IStoredApiToken, scopeArg: interfaces.data.TApiTokenScope) => storedTokenArg.scopes.includes(scopeArg),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
new ConfigHandler(opsServerRef);
|
||||||
|
return router;
|
||||||
|
};
|
||||||
|
|
||||||
|
tap.test('ConfigHandler accepts API token with config:read', async () => {
|
||||||
|
const router = makeOpsServer(['config:read']);
|
||||||
|
const result = await fireTypedRequest(router, 'getConfiguration', {
|
||||||
|
apiToken: 'valid-token',
|
||||||
|
});
|
||||||
|
expect(result.error).toBeUndefined();
|
||||||
|
expect(result.response.config.system.baseDir).toEqual('/tmp/dcrouter-home');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('ConfigHandler rejects API token without config:read', async () => {
|
||||||
|
const router = makeOpsServer(['logs:read']);
|
||||||
|
const result = await fireTypedRequest(router, 'getConfiguration', {
|
||||||
|
apiToken: 'valid-token',
|
||||||
|
});
|
||||||
|
expect(result.error?.text).toEqual('insufficient scope');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
|
|
||||||
|
import { createMigrationRunner } from '../ts_migrations/index.js';
|
||||||
|
|
||||||
|
function setPath(target: Record<string, any>, path: string, value: unknown): void {
|
||||||
|
const parts = path.split('.');
|
||||||
|
let cursor = target;
|
||||||
|
for (const part of parts.slice(0, -1)) {
|
||||||
|
cursor[part] = cursor[part] || {};
|
||||||
|
cursor = cursor[part];
|
||||||
|
}
|
||||||
|
cursor[parts[parts.length - 1]] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applySet(document: Record<string, any>, set: Record<string, unknown>): void {
|
||||||
|
for (const [key, value] of Object.entries(set)) {
|
||||||
|
setPath(document, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFakeDb(currentVersion: string) {
|
||||||
|
const ledgerDocument = {
|
||||||
|
nameId: 'smartmigration:smartmigration',
|
||||||
|
data: {
|
||||||
|
currentVersion,
|
||||||
|
steps: {},
|
||||||
|
lock: { holder: null, acquiredAt: null, expiresAt: null },
|
||||||
|
checkpoints: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const emptyCollection = {
|
||||||
|
find: () => ({
|
||||||
|
async *[Symbol.asyncIterator]() {},
|
||||||
|
}),
|
||||||
|
updateMany: async () => ({ modifiedCount: 0 }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const ledgerCollection = {
|
||||||
|
createIndex: async () => undefined,
|
||||||
|
findOne: async () => structuredClone(ledgerDocument),
|
||||||
|
findOneAndUpdate: async (_query: unknown, update: any) => {
|
||||||
|
applySet(ledgerDocument, update.$set || {});
|
||||||
|
return structuredClone(ledgerDocument);
|
||||||
|
},
|
||||||
|
updateOne: async (_query: unknown, update: any) => {
|
||||||
|
applySet(ledgerDocument, update.$set || {});
|
||||||
|
return { matchedCount: 1, modifiedCount: 1, upsertedCount: 0 };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
mongoDb: {
|
||||||
|
collection: (name: string) =>
|
||||||
|
name === 'SmartdataEasyStore' ? ledgerCollection : emptyCollection,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tap.test('migration runner bridges old package-version targets without real schema steps', async () => {
|
||||||
|
const runner = await createMigrationRunner(createFakeDb('13.16.0'), '13.31.0');
|
||||||
|
const result = await runner.run();
|
||||||
|
|
||||||
|
expect(result.currentVersionBefore).toEqual('13.16.0');
|
||||||
|
expect(result.currentVersionAfter).toEqual('13.31.0');
|
||||||
|
expect(result.stepsApplied).toHaveLength(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
|
import { requireOpsAuth } from '../ts/opsserver/helpers/auth.js';
|
||||||
|
import * as interfaces from '../ts_interfaces/index.js';
|
||||||
|
|
||||||
|
type TScope = interfaces.data.TApiTokenScope;
|
||||||
|
|
||||||
|
const makeIdentity = (role: string = 'user'): interfaces.data.IIdentity => ({
|
||||||
|
jwt: `jwt-${role}`,
|
||||||
|
userId: `${role}-user`,
|
||||||
|
name: role,
|
||||||
|
expiresAt: Date.now() + 3600000,
|
||||||
|
role,
|
||||||
|
});
|
||||||
|
|
||||||
|
const makeOpsServer = (options: {
|
||||||
|
identityRole?: string | null;
|
||||||
|
tokenScopes?: TScope[];
|
||||||
|
tokenPolicy?: interfaces.data.IApiTokenPolicy;
|
||||||
|
}) => {
|
||||||
|
const token = {
|
||||||
|
id: 'token-1',
|
||||||
|
name: 'test-token',
|
||||||
|
tokenHash: 'hash',
|
||||||
|
scopes: options.tokenScopes || [],
|
||||||
|
policy: options.tokenPolicy,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
expiresAt: null,
|
||||||
|
lastUsedAt: null,
|
||||||
|
createdBy: 'token-user',
|
||||||
|
enabled: true,
|
||||||
|
} as interfaces.data.IStoredApiToken;
|
||||||
|
|
||||||
|
return {
|
||||||
|
adminHandler: {
|
||||||
|
validateIdentity: async (identityArg?: interfaces.data.IIdentity) => {
|
||||||
|
if (!identityArg || options.identityRole === null) return null;
|
||||||
|
return { ...identityArg, role: options.identityRole || identityArg.role || 'user' };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dcRouterRef: {
|
||||||
|
apiTokenManager: {
|
||||||
|
validateToken: async (rawTokenArg: string) => rawTokenArg === 'valid-token' ? token : null,
|
||||||
|
hasScope: (storedTokenArg: interfaces.data.IStoredApiToken, scopeArg: TScope) => {
|
||||||
|
if (storedTokenArg.policy?.role === 'admin') return true;
|
||||||
|
return storedTokenArg.scopes.includes('*') || storedTokenArg.scopes.includes(scopeArg) || Boolean(storedTokenArg.policy?.scopes?.includes(scopeArg));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getErrorText = (errorArg: unknown) => {
|
||||||
|
return (errorArg as any).errorText || (errorArg as any).text || (errorArg as Error).message;
|
||||||
|
};
|
||||||
|
|
||||||
|
tap.test('requireOpsAuth accepts valid JWT identity for read endpoints', async () => {
|
||||||
|
const auth = await requireOpsAuth(
|
||||||
|
makeOpsServer({ identityRole: 'user' }),
|
||||||
|
{ identity: makeIdentity('user') },
|
||||||
|
{ scope: 'config:read' },
|
||||||
|
);
|
||||||
|
expect(auth.type).toEqual('identity');
|
||||||
|
expect(auth.userId).toEqual('user-user');
|
||||||
|
expect(auth.isAdmin).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('requireOpsAuth rejects non-admin JWT identity for admin identity requirements', async () => {
|
||||||
|
let errorText = '';
|
||||||
|
try {
|
||||||
|
await requireOpsAuth(
|
||||||
|
makeOpsServer({ identityRole: 'user' }),
|
||||||
|
{ identity: makeIdentity('user') },
|
||||||
|
{ scope: 'routes:write', requireAdminIdentity: true },
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
errorText = getErrorText(error);
|
||||||
|
}
|
||||||
|
expect(errorText).toEqual('admin identity required');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('requireOpsAuth accepts scoped API tokens', async () => {
|
||||||
|
const auth = await requireOpsAuth(
|
||||||
|
makeOpsServer({ identityRole: null, tokenScopes: ['logs:read'] }),
|
||||||
|
{ apiToken: 'valid-token' },
|
||||||
|
{ scope: 'logs:read' },
|
||||||
|
);
|
||||||
|
expect(auth.type).toEqual('apiToken');
|
||||||
|
expect(auth.userId).toEqual('token-user');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('requireOpsAuth rejects API tokens without the required scope', async () => {
|
||||||
|
let errorText = '';
|
||||||
|
try {
|
||||||
|
await requireOpsAuth(
|
||||||
|
makeOpsServer({ identityRole: null, tokenScopes: ['logs:read'] }),
|
||||||
|
{ apiToken: 'valid-token' },
|
||||||
|
{ scope: 'stats:read' },
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
errorText = getErrorText(error);
|
||||||
|
}
|
||||||
|
expect(errorText).toEqual('insufficient scope');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('requireOpsAuth requires admin policy for sensitive API-token operations', async () => {
|
||||||
|
let errorText = '';
|
||||||
|
try {
|
||||||
|
await requireOpsAuth(
|
||||||
|
makeOpsServer({ identityRole: null, tokenScopes: ['tokens:manage'] }),
|
||||||
|
{ apiToken: 'valid-token' },
|
||||||
|
{ scope: 'tokens:manage', requireAdminToken: true },
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
errorText = getErrorText(error);
|
||||||
|
}
|
||||||
|
expect(errorText).toEqual('admin API token required');
|
||||||
|
|
||||||
|
const auth = await requireOpsAuth(
|
||||||
|
makeOpsServer({ identityRole: null, tokenPolicy: { role: 'admin' } }),
|
||||||
|
{ apiToken: 'valid-token' },
|
||||||
|
{ scope: 'tokens:manage', requireAdminToken: true },
|
||||||
|
);
|
||||||
|
expect(auth.isAdmin).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
@@ -136,6 +136,9 @@ const setupHandler = (options: {
|
|||||||
const opsServerRef: any = {
|
const opsServerRef: any = {
|
||||||
typedrouter,
|
typedrouter,
|
||||||
adminHandler: {
|
adminHandler: {
|
||||||
|
validateIdentity: async (identity: interfaces.data.IIdentity) => options.isAdmin
|
||||||
|
? { ...identity, role: 'admin' }
|
||||||
|
: identity,
|
||||||
adminIdentityGuard: {
|
adminIdentityGuard: {
|
||||||
exec: async () => Boolean(options.isAdmin),
|
exec: async () => Boolean(options.isAdmin),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import * as plugins from '../plugins.js';
|
|||||||
import * as paths from '../paths.js';
|
import * as paths from '../paths.js';
|
||||||
import * as handlers from './handlers/index.js';
|
import * as handlers from './handlers/index.js';
|
||||||
import * as interfaces from '../../ts_interfaces/index.js';
|
import * as interfaces from '../../ts_interfaces/index.js';
|
||||||
import { requireValidIdentity, requireAdminIdentity } from './helpers/guards.js';
|
|
||||||
|
|
||||||
export class OpsServer {
|
export class OpsServer {
|
||||||
public dcRouterRef: DcRouter;
|
public dcRouterRef: DcRouter;
|
||||||
@@ -12,9 +11,9 @@ export class OpsServer {
|
|||||||
// Main TypedRouter — unauthenticated endpoints (login/logout/verify) and own-auth handlers
|
// Main TypedRouter — unauthenticated endpoints (login/logout/verify) and own-auth handlers
|
||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
|
|
||||||
// Auth-enforced routers — middleware validates identity before any handler runs
|
// Grouped routers. Handlers enforce auth explicitly with per-endpoint scopes.
|
||||||
public viewRouter = new plugins.typedrequest.TypedRouter<{ request: { identity: interfaces.data.IIdentity } }>();
|
public viewRouter = new plugins.typedrequest.TypedRouter<{ request: { identity?: interfaces.data.IIdentity; apiToken?: string } }>();
|
||||||
public adminRouter = new plugins.typedrequest.TypedRouter<{ request: { identity: interfaces.data.IIdentity } }>();
|
public adminRouter = new plugins.typedrequest.TypedRouter<{ request: { identity?: interfaces.data.IIdentity; apiToken?: string } }>();
|
||||||
|
|
||||||
// Handler instances
|
// Handler instances
|
||||||
public adminHandler!: handlers.AdminHandler;
|
public adminHandler!: handlers.AdminHandler;
|
||||||
@@ -72,16 +71,6 @@ export class OpsServer {
|
|||||||
this.adminHandler = new handlers.AdminHandler(this);
|
this.adminHandler = new handlers.AdminHandler(this);
|
||||||
await this.adminHandler.initialize();
|
await this.adminHandler.initialize();
|
||||||
|
|
||||||
// viewRouter middleware: requires valid identity (any logged-in user)
|
|
||||||
this.viewRouter.addMiddleware(async (typedRequest) => {
|
|
||||||
await requireValidIdentity(this.adminHandler, typedRequest.request);
|
|
||||||
});
|
|
||||||
|
|
||||||
// adminRouter middleware: requires admin identity
|
|
||||||
this.adminRouter.addMiddleware(async (typedRequest) => {
|
|
||||||
await requireAdminIdentity(this.adminHandler, typedRequest.request);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect auth routers to the main typedrouter
|
// Connect auth routers to the main typedrouter
|
||||||
this.typedrouter.addTypedRouter(this.viewRouter);
|
this.typedrouter.addTypedRouter(this.viewRouter);
|
||||||
this.typedrouter.addTypedRouter(this.adminRouter);
|
this.typedrouter.addTypedRouter(this.adminRouter);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRUD handler for the singleton `AcmeConfigDoc`.
|
* CRUD handler for the singleton `AcmeConfigDoc`.
|
||||||
@@ -20,29 +21,11 @@ export class AcmeConfigHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -258,12 +258,18 @@ export class AdminHandler {
|
|||||||
this.opsServerRef.adminRouter.addTypedHandler(
|
this.opsServerRef.adminRouter.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateInitialAdminUser>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateInitialAdminUser>(
|
||||||
'createInitialAdminUser',
|
'createInitialAdminUser',
|
||||||
async (dataArg) => this.createInitialAdminUser({
|
async (dataArg) => {
|
||||||
|
const isAdmin = await this.adminIdentityGuard.exec({ identity: dataArg.identity });
|
||||||
|
if (!isAdmin) {
|
||||||
|
throw new plugins.typedrequest.TypedResponseError('admin identity required');
|
||||||
|
}
|
||||||
|
return this.createInitialAdminUser({
|
||||||
email: dataArg.email,
|
email: dataArg.email,
|
||||||
name: dataArg.name,
|
name: dataArg.name,
|
||||||
password: dataArg.password,
|
password: dataArg.password,
|
||||||
enableIdpGlobalAuth: dataArg.enableIdpGlobalAuth,
|
enableIdpGlobalAuth: dataArg.enableIdpGlobalAuth,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -300,8 +306,10 @@ export class AdminHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_AdminLogout>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_AdminLogout>(
|
||||||
'adminLogout',
|
'adminLogout',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
// In a real implementation, you might want to blacklist the JWT
|
const identity = await this.validateIdentity(dataArg.identity);
|
||||||
// For now, just return success
|
if (!identity) {
|
||||||
|
throw new plugins.typedrequest.TypedResponseError('identity is not valid');
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
@@ -314,52 +322,8 @@ export class AdminHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_VerifyIdentity>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_VerifyIdentity>(
|
||||||
'verifyIdentity',
|
'verifyIdentity',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
if (!dataArg.identity?.jwt) {
|
const identity = await this.validateIdentity(dataArg.identity);
|
||||||
return {
|
return identity ? { valid: true, identity } : { valid: false };
|
||||||
valid: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const jwtData = await this.smartjwtInstance.verifyJWTAndGetData(dataArg.identity.jwt);
|
|
||||||
|
|
||||||
// Check if expired
|
|
||||||
if (jwtData.expiresAt < Date.now()) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if logged in
|
|
||||||
if (jwtData.status !== 'loggedIn') {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await this.resolveUser(jwtData.userId);
|
|
||||||
if (!user) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
valid: true,
|
|
||||||
identity: {
|
|
||||||
jwt: dataArg.identity.jwt,
|
|
||||||
userId: user.id,
|
|
||||||
name: user.name || user.username,
|
|
||||||
expiresAt: jwtData.expiresAt,
|
|
||||||
role: user.role,
|
|
||||||
type: 'user',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -372,45 +336,7 @@ export class AdminHandler {
|
|||||||
identity: interfaces.data.IIdentity;
|
identity: interfaces.data.IIdentity;
|
||||||
}>(
|
}>(
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
if (!dataArg.identity?.jwt) {
|
return Boolean(await this.validateIdentity(dataArg.identity));
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const jwtData = await this.smartjwtInstance.verifyJWTAndGetData(dataArg.identity.jwt);
|
|
||||||
|
|
||||||
// Check expiration
|
|
||||||
if (jwtData.expiresAt < Date.now()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check status
|
|
||||||
if (jwtData.status !== 'loggedIn') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify data hasn't been tampered with
|
|
||||||
if (dataArg.identity.expiresAt !== jwtData.expiresAt) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataArg.identity.userId !== jwtData.userId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await this.resolveUser(jwtData.userId);
|
|
||||||
if (!user) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataArg.identity.role && dataArg.identity.role !== user.role) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
failedHint: 'identity is not valid',
|
failedHint: 'identity is not valid',
|
||||||
@@ -425,14 +351,8 @@ export class AdminHandler {
|
|||||||
identity: interfaces.data.IIdentity;
|
identity: interfaces.data.IIdentity;
|
||||||
}>(
|
}>(
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
// First check if identity is valid
|
const identity = await this.validateIdentity(dataArg.identity);
|
||||||
const isValid = await this.validIdentityGuard.exec(dataArg);
|
return identity?.role === 'admin';
|
||||||
if (!isValid) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user has admin role
|
|
||||||
return dataArg.identity.role === 'admin';
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
failedHint: 'user is not admin',
|
failedHint: 'user is not admin',
|
||||||
@@ -440,6 +360,49 @@ export class AdminHandler {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public async validateIdentity(
|
||||||
|
identityArg?: interfaces.data.IIdentity,
|
||||||
|
): Promise<interfaces.data.IIdentity | null> {
|
||||||
|
if (!identityArg?.jwt) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const jwtData = await this.smartjwtInstance.verifyJWTAndGetData(identityArg.jwt);
|
||||||
|
if (jwtData.expiresAt < Date.now()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (jwtData.status !== 'loggedIn') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (identityArg.expiresAt !== jwtData.expiresAt) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (identityArg.userId !== jwtData.userId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.resolveUser(jwtData.userId);
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (identityArg.role && identityArg.role !== user.role) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
jwt: identityArg.jwt,
|
||||||
|
userId: user.id,
|
||||||
|
name: user.name || user.username,
|
||||||
|
expiresAt: jwtData.expiresAt,
|
||||||
|
role: user.role,
|
||||||
|
type: 'user',
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async authenticateUser(optionsArg: {
|
private async authenticateUser(optionsArg: {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class ApiTokenHandler {
|
export class ApiTokenHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -17,6 +18,11 @@ export class ApiTokenHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateApiToken>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateApiToken>(
|
||||||
'createApiToken',
|
'createApiToken',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'tokens:manage',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'Token management not initialized' };
|
return { success: false, message: 'Token management not initialized' };
|
||||||
@@ -25,7 +31,7 @@ export class ApiTokenHandler {
|
|||||||
dataArg.name,
|
dataArg.name,
|
||||||
dataArg.scopes,
|
dataArg.scopes,
|
||||||
dataArg.expiresInDays ?? null,
|
dataArg.expiresInDays ?? null,
|
||||||
dataArg.identity.userId,
|
auth.userId,
|
||||||
dataArg.policy,
|
dataArg.policy,
|
||||||
);
|
);
|
||||||
return { success: true, tokenId: result.id, tokenValue: result.rawToken };
|
return { success: true, tokenId: result.id, tokenValue: result.rawToken };
|
||||||
@@ -38,6 +44,11 @@ export class ApiTokenHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListApiTokens>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListApiTokens>(
|
||||||
'listApiTokens',
|
'listApiTokens',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'tokens:read',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { tokens: [] };
|
return { tokens: [] };
|
||||||
@@ -52,6 +63,11 @@ export class ApiTokenHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RevokeApiToken>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RevokeApiToken>(
|
||||||
'revokeApiToken',
|
'revokeApiToken',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'tokens:manage',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'Token management not initialized' };
|
return { success: false, message: 'Token management not initialized' };
|
||||||
@@ -67,6 +83,11 @@ export class ApiTokenHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RollApiToken>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RollApiToken>(
|
||||||
'rollApiToken',
|
'rollApiToken',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'tokens:manage',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'Token management not initialized' };
|
return { success: false, message: 'Token management not initialized' };
|
||||||
@@ -85,6 +106,11 @@ export class ApiTokenHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ToggleApiToken>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ToggleApiToken>(
|
||||||
'toggleApiToken',
|
'toggleApiToken',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'tokens:manage',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
const manager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'Token management not initialized' };
|
return { success: false, message: 'Token management not initialized' };
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { OpsServer } from '../classes.opsserver.js';
|
|||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
import { AcmeCertDoc, ProxyCertDoc } from '../../db/index.js';
|
import { AcmeCertDoc, ProxyCertDoc } from '../../db/index.js';
|
||||||
import { logger } from '../../logger.js';
|
import { logger } from '../../logger.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mirrors `SmartacmeCertMatcher.getCertificateDomainNameByDomainName` from
|
* Mirrors `SmartacmeCertMatcher.getCertificateDomainNameByDomainName` from
|
||||||
@@ -37,29 +38,11 @@ export class CertificateHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
|
|||||||
import * as paths from '../../paths.js';
|
import * as paths from '../../paths.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class ConfigHandler {
|
export class ConfigHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -17,6 +18,7 @@ export class ConfigHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetConfiguration>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetConfiguration>(
|
||||||
'getConfiguration',
|
'getConfiguration',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'config:read' });
|
||||||
const config = await this.getConfiguration();
|
const config = await this.getConfiguration();
|
||||||
return {
|
return {
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRUD + connection-test handlers for DnsProviderDoc.
|
* CRUD + connection-test handlers for DnsProviderDoc.
|
||||||
@@ -20,29 +21,11 @@ export class DnsProviderHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRUD handlers for DnsRecordDoc.
|
* CRUD handlers for DnsRecordDoc.
|
||||||
@@ -17,29 +18,11 @@ export class DnsRecordHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRUD handlers for DomainDoc.
|
* CRUD handlers for DomainDoc.
|
||||||
@@ -17,29 +18,11 @@ export class DomainHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRUD + DNS provisioning handler for email domains.
|
* CRUD + DNS provisioning handler for email domains.
|
||||||
@@ -19,29 +20,11 @@ export class EmailDomainHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get manager() {
|
private get manager() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class EmailOpsHandler {
|
export class EmailOpsHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -18,6 +19,7 @@ export class EmailOpsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetAllEmails>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetAllEmails>(
|
||||||
'getAllEmails',
|
'getAllEmails',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'emails:read' });
|
||||||
const emails = this.getAllQueueEmails();
|
const emails = this.getAllQueueEmails();
|
||||||
return { emails };
|
return { emails };
|
||||||
}
|
}
|
||||||
@@ -29,6 +31,7 @@ export class EmailOpsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetEmailDetail>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetEmailDetail>(
|
||||||
'getEmailDetail',
|
'getEmailDetail',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'emails:read' });
|
||||||
const email = this.getEmailDetail(dataArg.emailId);
|
const email = this.getEmailDetail(dataArg.emailId);
|
||||||
return { email };
|
return { email };
|
||||||
}
|
}
|
||||||
@@ -42,6 +45,10 @@ export class EmailOpsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ResendEmail>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ResendEmail>(
|
||||||
'resendEmail',
|
'resendEmail',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'emails:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
||||||
if (!emailServer?.deliveryQueue) {
|
if (!emailServer?.deliveryQueue) {
|
||||||
return { success: false, error: 'Email server not available' };
|
return { success: false, error: 'Email server not available' };
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
|
|||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
import { logBuffer, baseLogger } from '../../logger.js';
|
import { logBuffer, baseLogger } from '../../logger.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
// Module-level singleton: the log push destination is added once and reuses
|
// Module-level singleton: the log push destination is added once and reuses
|
||||||
// the current OpsServer reference so it survives OpsServer restarts without
|
// the current OpsServer reference so it survives OpsServer restarts without
|
||||||
@@ -40,6 +41,7 @@ export class LogsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRecentLogs>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRecentLogs>(
|
||||||
'getRecentLogs',
|
'getRecentLogs',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'logs:read' });
|
||||||
const logs = await this.getRecentLogs(
|
const logs = await this.getRecentLogs(
|
||||||
dataArg.level,
|
dataArg.level,
|
||||||
dataArg.category,
|
dataArg.category,
|
||||||
@@ -63,6 +65,7 @@ export class LogsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetLogStream>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetLogStream>(
|
||||||
'getLogStream',
|
'getLogStream',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'logs:read' });
|
||||||
// Create a virtual stream for log streaming
|
// Create a virtual stream for log streaming
|
||||||
const virtualStream = new plugins.typedrequest.VirtualStream<Uint8Array>();
|
const virtualStream = new plugins.typedrequest.VirtualStream<Uint8Array>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class NetworkTargetHandler {
|
export class NetworkTargetHandler {
|
||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
@@ -14,29 +15,11 @@ export class NetworkTargetHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class RadiusHandler {
|
export class RadiusHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -19,6 +20,7 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusClients>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusClients>(
|
||||||
'getRadiusClients',
|
'getRadiusClients',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'radius:read' });
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -43,6 +45,10 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SetRadiusClient>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SetRadiusClient>(
|
||||||
'setRadiusClient',
|
'setRadiusClient',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'radius:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -64,6 +70,10 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RemoveRadiusClient>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RemoveRadiusClient>(
|
||||||
'removeRadiusClient',
|
'removeRadiusClient',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'radius:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -88,6 +98,7 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVlanMappings>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVlanMappings>(
|
||||||
'getVlanMappings',
|
'getVlanMappings',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'radius:read' });
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -124,6 +135,10 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SetVlanMapping>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SetVlanMapping>(
|
||||||
'setVlanMapping',
|
'setVlanMapping',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'radius:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -156,6 +171,10 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RemoveVlanMapping>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RemoveVlanMapping>(
|
||||||
'removeVlanMapping',
|
'removeVlanMapping',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'radius:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -177,6 +196,10 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateVlanConfig>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateVlanConfig>(
|
||||||
'updateVlanConfig',
|
'updateVlanConfig',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'radius:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -209,6 +232,7 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_TestVlanAssignment>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_TestVlanAssignment>(
|
||||||
'testVlanAssignment',
|
'testVlanAssignment',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'radius:read' });
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -243,6 +267,7 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusSessions>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusSessions>(
|
||||||
'getRadiusSessions',
|
'getRadiusSessions',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'radius:read' });
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -292,6 +317,10 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DisconnectRadiusSession>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DisconnectRadiusSession>(
|
||||||
'disconnectRadiusSession',
|
'disconnectRadiusSession',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'radius:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -317,6 +346,7 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusAccountingSummary>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusAccountingSummary>(
|
||||||
'getRadiusAccountingSummary',
|
'getRadiusAccountingSummary',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'radius:read' });
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
@@ -354,6 +384,7 @@ export class RadiusHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusStatistics>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusStatistics>(
|
||||||
'getRadiusStatistics',
|
'getRadiusStatistics',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'radius:read' });
|
||||||
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
|
||||||
|
|
||||||
if (!radiusServer) {
|
if (!radiusServer) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class RemoteIngressHandler {
|
export class RemoteIngressHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -18,6 +19,7 @@ export class RemoteIngressHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngresses>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngresses>(
|
||||||
'getRemoteIngresses',
|
'getRemoteIngresses',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'remote-ingress:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { edges: [] };
|
return { edges: [] };
|
||||||
@@ -46,6 +48,10 @@ export class RemoteIngressHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateRemoteIngress>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateRemoteIngress>(
|
||||||
'createRemoteIngress',
|
'createRemoteIngress',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'remote-ingress:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
||||||
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
||||||
|
|
||||||
@@ -78,6 +84,10 @@ export class RemoteIngressHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteRemoteIngress>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteRemoteIngress>(
|
||||||
'deleteRemoteIngress',
|
'deleteRemoteIngress',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'remote-ingress:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
||||||
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
||||||
|
|
||||||
@@ -103,6 +113,10 @@ export class RemoteIngressHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateRemoteIngress>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateRemoteIngress>(
|
||||||
'updateRemoteIngress',
|
'updateRemoteIngress',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'remote-ingress:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
||||||
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
||||||
|
|
||||||
@@ -148,6 +162,10 @@ export class RemoteIngressHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RegenerateRemoteIngressSecret>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RegenerateRemoteIngressSecret>(
|
||||||
'regenerateRemoteIngressSecret',
|
'regenerateRemoteIngressSecret',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'remote-ingress:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
||||||
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
||||||
|
|
||||||
@@ -175,6 +193,7 @@ export class RemoteIngressHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngressStatus>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngressStatus>(
|
||||||
'getRemoteIngressStatus',
|
'getRemoteIngressStatus',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'remote-ingress:read' });
|
||||||
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
const tunnelManager = this.opsServerRef.dcRouterRef.tunnelManager;
|
||||||
if (!tunnelManager) {
|
if (!tunnelManager) {
|
||||||
return { statuses: [] };
|
return { statuses: [] };
|
||||||
@@ -189,6 +208,10 @@ export class RemoteIngressHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngressConnectionToken>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRemoteIngressConnectionToken>(
|
||||||
'getRemoteIngressConnectionToken',
|
'getRemoteIngressConnectionToken',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'remote-ingress:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
const manager = this.opsServerRef.dcRouterRef.remoteIngressManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'RemoteIngress not configured' };
|
return { success: false, message: 'RemoteIngress not configured' };
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class RouteManagementHandler {
|
export class RouteManagementHandler {
|
||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
@@ -18,31 +19,11 @@ export class RouteManagementHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// Try JWT identity first
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
if (request.identity?.jwt) {
|
scope: requiredScope,
|
||||||
try {
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try API token
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
|
|||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
import { MetricsManager } from '../../monitoring/index.js';
|
import { MetricsManager } from '../../monitoring/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class SecurityHandler {
|
export class SecurityHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -17,6 +18,7 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecurityMetrics>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecurityMetrics>(
|
||||||
'getSecurityMetrics',
|
'getSecurityMetrics',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
|
||||||
const metrics = await this.collectSecurityMetrics();
|
const metrics = await this.collectSecurityMetrics();
|
||||||
return {
|
return {
|
||||||
metrics: {
|
metrics: {
|
||||||
@@ -43,6 +45,7 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetActiveConnections>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetActiveConnections>(
|
||||||
'getActiveConnections',
|
'getActiveConnections',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
const connections = await this.getActiveConnections(dataArg.protocol, dataArg.state);
|
const connections = await this.getActiveConnections(dataArg.protocol, dataArg.state);
|
||||||
const connectionInfos: interfaces.data.IConnectionInfo[] = connections.map(conn => ({
|
const connectionInfos: interfaces.data.IConnectionInfo[] = connections.map(conn => ({
|
||||||
id: conn.id,
|
id: conn.id,
|
||||||
@@ -82,6 +85,7 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetNetworkStats>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetNetworkStats>(
|
||||||
'getNetworkStats',
|
'getNetworkStats',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
// Get network stats from MetricsManager if available
|
// Get network stats from MetricsManager if available
|
||||||
if (this.opsServerRef.dcRouterRef.metricsManager) {
|
if (this.opsServerRef.dcRouterRef.metricsManager) {
|
||||||
const networkStats = await this.opsServerRef.dcRouterRef.metricsManager.getNetworkStats();
|
const networkStats = await this.opsServerRef.dcRouterRef.metricsManager.getNetworkStats();
|
||||||
@@ -136,6 +140,7 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRateLimitStatus>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRateLimitStatus>(
|
||||||
'getRateLimitStatus',
|
'getRateLimitStatus',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
|
||||||
const status = await this.getRateLimitStatus(dataArg.domain, dataArg.ip);
|
const status = await this.getRateLimitStatus(dataArg.domain, dataArg.ip);
|
||||||
const limits: interfaces.data.IRateLimitInfo[] = status.limits.map(limit => ({
|
const limits: interfaces.data.IRateLimitInfo[] = status.limits.map(limit => ({
|
||||||
domain: limit.identifier,
|
domain: limit.identifier,
|
||||||
@@ -161,7 +166,8 @@ export class SecurityHandler {
|
|||||||
router.addTypedHandler(
|
router.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListSecurityBlockRules>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListSecurityBlockRules>(
|
||||||
'listSecurityBlockRules',
|
'listSecurityBlockRules',
|
||||||
async () => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
return { rules: manager ? await manager.listBlockRules() : [] };
|
return { rules: manager ? await manager.listBlockRules() : [] };
|
||||||
},
|
},
|
||||||
@@ -171,7 +177,8 @@ export class SecurityHandler {
|
|||||||
router.addTypedHandler(
|
router.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListIpIntelligence>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListIpIntelligence>(
|
||||||
'listIpIntelligence',
|
'listIpIntelligence',
|
||||||
async () => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
return { records: manager ? await manager.listIpIntelligence() : [] };
|
return { records: manager ? await manager.listIpIntelligence() : [] };
|
||||||
},
|
},
|
||||||
@@ -181,7 +188,8 @@ export class SecurityHandler {
|
|||||||
router.addTypedHandler(
|
router.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetCompiledSecurityPolicy>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetCompiledSecurityPolicy>(
|
||||||
'getCompiledSecurityPolicy',
|
'getCompiledSecurityPolicy',
|
||||||
async () => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
return {
|
return {
|
||||||
policy: manager
|
policy: manager
|
||||||
@@ -196,6 +204,7 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListSecurityPolicyAudit>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListSecurityPolicyAudit>(
|
||||||
'listSecurityPolicyAudit',
|
'listSecurityPolicyAudit',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'security:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
return { events: manager ? await manager.listAuditEvents(dataArg.limit || 100) : [] };
|
return { events: manager ? await manager.listAuditEvents(dataArg.limit || 100) : [] };
|
||||||
},
|
},
|
||||||
@@ -208,6 +217,10 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateSecurityBlockRule>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateSecurityBlockRule>(
|
||||||
'createSecurityBlockRule',
|
'createSecurityBlockRule',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'security:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
||||||
const rule = await manager.createBlockRule({
|
const rule = await manager.createBlockRule({
|
||||||
@@ -216,7 +229,7 @@ export class SecurityHandler {
|
|||||||
matchMode: dataArg.matchMode,
|
matchMode: dataArg.matchMode,
|
||||||
reason: dataArg.reason,
|
reason: dataArg.reason,
|
||||||
enabled: dataArg.enabled,
|
enabled: dataArg.enabled,
|
||||||
}, dataArg.identity.userId);
|
}, auth.userId);
|
||||||
return { success: true, rule };
|
return { success: true, rule };
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -226,6 +239,10 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateSecurityBlockRule>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateSecurityBlockRule>(
|
||||||
'updateSecurityBlockRule',
|
'updateSecurityBlockRule',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'security:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
||||||
const rule = await manager.updateBlockRule(dataArg.id, {
|
const rule = await manager.updateBlockRule(dataArg.id, {
|
||||||
@@ -233,7 +250,7 @@ export class SecurityHandler {
|
|||||||
matchMode: dataArg.matchMode,
|
matchMode: dataArg.matchMode,
|
||||||
reason: dataArg.reason,
|
reason: dataArg.reason,
|
||||||
enabled: dataArg.enabled,
|
enabled: dataArg.enabled,
|
||||||
}, dataArg.identity.userId);
|
}, auth.userId);
|
||||||
return rule ? { success: true, rule } : { success: false, message: 'Rule not found' };
|
return rule ? { success: true, rule } : { success: false, message: 'Rule not found' };
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -243,9 +260,13 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteSecurityBlockRule>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteSecurityBlockRule>(
|
||||||
'deleteSecurityBlockRule',
|
'deleteSecurityBlockRule',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'security:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
||||||
const success = await manager.deleteBlockRule(dataArg.id, dataArg.identity.userId);
|
const success = await manager.deleteBlockRule(dataArg.id, auth.userId);
|
||||||
return { success, message: success ? undefined : 'Rule not found' };
|
return { success, message: success ? undefined : 'Rule not found' };
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -255,6 +276,10 @@ export class SecurityHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RefreshIpIntelligence>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RefreshIpIntelligence>(
|
||||||
'refreshIpIntelligence',
|
'refreshIpIntelligence',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'security:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
const manager = this.opsServerRef.dcRouterRef.securityPolicyManager;
|
||||||
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
if (!manager) return { success: false, message: 'Security policy manager not initialized' };
|
||||||
const record = await manager.refreshIpIntelligence(dataArg.ipAddress);
|
const record = await manager.refreshIpIntelligence(dataArg.ipAddress);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class SourceProfileHandler {
|
export class SourceProfileHandler {
|
||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
@@ -14,29 +15,11 @@ export class SourceProfileHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as interfaces from '../../../ts_interfaces/index.js';
|
|||||||
import { MetricsManager } from '../../monitoring/index.js';
|
import { MetricsManager } from '../../monitoring/index.js';
|
||||||
import { SecurityLogger } from '../../security/classes.securitylogger.js';
|
import { SecurityLogger } from '../../security/classes.securitylogger.js';
|
||||||
import { commitinfo } from '../../00_commitinfo_data.js';
|
import { commitinfo } from '../../00_commitinfo_data.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class StatsHandler {
|
export class StatsHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -19,6 +20,7 @@ export class StatsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServerStatistics>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServerStatistics>(
|
||||||
'getServerStatistics',
|
'getServerStatistics',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
const stats = await this.collectServerStats();
|
const stats = await this.collectServerStats();
|
||||||
return {
|
return {
|
||||||
stats: {
|
stats: {
|
||||||
@@ -42,6 +44,7 @@ export class StatsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetEmailStatistics>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetEmailStatistics>(
|
||||||
'getEmailStatistics',
|
'getEmailStatistics',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
||||||
if (!emailServer) {
|
if (!emailServer) {
|
||||||
return {
|
return {
|
||||||
@@ -81,6 +84,7 @@ export class StatsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsStatistics>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsStatistics>(
|
||||||
'getDnsStatistics',
|
'getDnsStatistics',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
const dnsServer = this.opsServerRef.dcRouterRef.dnsServer;
|
const dnsServer = this.opsServerRef.dcRouterRef.dnsServer;
|
||||||
if (!dnsServer) {
|
if (!dnsServer) {
|
||||||
return {
|
return {
|
||||||
@@ -118,6 +122,7 @@ export class StatsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetQueueStatus>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetQueueStatus>(
|
||||||
'getQueueStatus',
|
'getQueueStatus',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
||||||
const queues: interfaces.data.IQueueStatus[] = [];
|
const queues: interfaces.data.IQueueStatus[] = [];
|
||||||
|
|
||||||
@@ -146,6 +151,7 @@ export class StatsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetHealthStatus>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetHealthStatus>(
|
||||||
'getHealthStatus',
|
'getHealthStatus',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
const health = await this.checkHealthStatus();
|
const health = await this.checkHealthStatus();
|
||||||
return {
|
return {
|
||||||
health: {
|
health: {
|
||||||
@@ -171,6 +177,7 @@ export class StatsHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetCombinedMetrics>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetCombinedMetrics>(
|
||||||
'getCombinedMetrics',
|
'getCombinedMetrics',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'stats:read' });
|
||||||
const sections = dataArg.sections || {
|
const sections = dataArg.sections || {
|
||||||
server: true,
|
server: true,
|
||||||
email: true,
|
email: true,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class TargetProfileHandler {
|
export class TargetProfileHandler {
|
||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
@@ -14,29 +15,11 @@ export class TargetProfileHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.apiToken) {
|
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
|
||||||
if (tokenManager) {
|
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
|
||||||
if (token) {
|
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
|
||||||
return token.createdBy;
|
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for OpsServer user accounts. Registers on adminRouter,
|
* Handler for OpsServer user accounts. Registers on adminRouter,
|
||||||
@@ -20,7 +21,12 @@ export class UsersHandler {
|
|||||||
router.addTypedHandler(
|
router.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListUsers>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListUsers>(
|
||||||
'listUsers',
|
'listUsers',
|
||||||
async (_dataArg) => {
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'users:read',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
const users = await this.opsServerRef.adminHandler.listUsers();
|
const users = await this.opsServerRef.adminHandler.listUsers();
|
||||||
return { users };
|
return { users };
|
||||||
},
|
},
|
||||||
@@ -30,23 +36,37 @@ export class UsersHandler {
|
|||||||
router.addTypedHandler(
|
router.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateUser>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateUser>(
|
||||||
'createUser',
|
'createUser',
|
||||||
async (dataArg) => this.opsServerRef.adminHandler.createUser({
|
async (dataArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'users:manage',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
|
return this.opsServerRef.adminHandler.createUser({
|
||||||
email: dataArg.email,
|
email: dataArg.email,
|
||||||
name: dataArg.name,
|
name: dataArg.name,
|
||||||
role: dataArg.role,
|
role: dataArg.role,
|
||||||
password: dataArg.password,
|
password: dataArg.password,
|
||||||
enableIdpGlobalAuth: dataArg.enableIdpGlobalAuth,
|
enableIdpGlobalAuth: dataArg.enableIdpGlobalAuth,
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
router.addTypedHandler(
|
router.addTypedHandler(
|
||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteUser>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteUser>(
|
||||||
'deleteUser',
|
'deleteUser',
|
||||||
async (dataArg) => this.opsServerRef.adminHandler.deleteUser({
|
async (dataArg) => {
|
||||||
|
const auth = await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'users:manage',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
requireAdminToken: true,
|
||||||
|
});
|
||||||
|
return this.opsServerRef.adminHandler.deleteUser({
|
||||||
id: dataArg.id,
|
id: dataArg.id,
|
||||||
requestingUserId: dataArg.identity.userId,
|
requestingUserId: auth.userId,
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
export class VpnHandler {
|
export class VpnHandler {
|
||||||
constructor(private opsServerRef: OpsServer) {
|
constructor(private opsServerRef: OpsServer) {
|
||||||
@@ -18,6 +19,7 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnClients>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnClients>(
|
||||||
'getVpnClients',
|
'getVpnClients',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'vpn:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { clients: [] };
|
return { clients: [] };
|
||||||
@@ -49,6 +51,7 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnStatus>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnStatus>(
|
||||||
'getVpnStatus',
|
'getVpnStatus',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'vpn:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
const vpnConfig = this.opsServerRef.dcRouterRef.options.vpnConfig;
|
const vpnConfig = this.opsServerRef.dcRouterRef.options.vpnConfig;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
@@ -84,6 +87,7 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnConnectedClients>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnConnectedClients>(
|
||||||
'getVpnConnectedClients',
|
'getVpnConnectedClients',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'vpn:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { connectedClients: [] };
|
return { connectedClients: [] };
|
||||||
@@ -111,6 +115,10 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateVpnClient>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateVpnClient>(
|
||||||
'createVpnClient',
|
'createVpnClient',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'vpn:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
@@ -168,6 +176,10 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateVpnClient>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateVpnClient>(
|
||||||
'updateVpnClient',
|
'updateVpnClient',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'vpn:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
@@ -198,6 +210,10 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteVpnClient>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteVpnClient>(
|
||||||
'deleteVpnClient',
|
'deleteVpnClient',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'vpn:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
@@ -218,6 +234,10 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_EnableVpnClient>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_EnableVpnClient>(
|
||||||
'enableVpnClient',
|
'enableVpnClient',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'vpn:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
@@ -238,6 +258,10 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DisableVpnClient>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DisableVpnClient>(
|
||||||
'disableVpnClient',
|
'disableVpnClient',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'vpn:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
@@ -258,6 +282,10 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RotateVpnClientKey>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RotateVpnClientKey>(
|
||||||
'rotateVpnClientKey',
|
'rotateVpnClientKey',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'vpn:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
@@ -281,6 +309,10 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ExportVpnClientConfig>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ExportVpnClientConfig>(
|
||||||
'exportVpnClientConfig',
|
'exportVpnClientConfig',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, {
|
||||||
|
scope: 'vpn:write',
|
||||||
|
requireAdminIdentity: true,
|
||||||
|
});
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
@@ -301,6 +333,7 @@ export class VpnHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnClientTelemetry>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVpnClientTelemetry>(
|
||||||
'getVpnClientTelemetry',
|
'getVpnClientTelemetry',
|
||||||
async (dataArg, toolsArg) => {
|
async (dataArg, toolsArg) => {
|
||||||
|
await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'vpn:read' });
|
||||||
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
const manager = this.opsServerRef.dcRouterRef.vpnManager;
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return { success: false, message: 'VPN not configured' };
|
return { success: false, message: 'VPN not configured' };
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import type { OpsServer } from '../classes.opsserver.js';
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
import { requireOpsAuth } from '../helpers/auth.js';
|
||||||
|
|
||||||
type TAuthContext = {
|
type TAuthContext = {
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -20,39 +21,23 @@ export class WorkHosterHandler {
|
|||||||
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
requiredScope?: interfaces.data.TApiTokenScope,
|
requiredScope?: interfaces.data.TApiTokenScope,
|
||||||
): Promise<TAuthContext> {
|
): Promise<TAuthContext> {
|
||||||
if (request.identity?.jwt) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
try {
|
scope: requiredScope,
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
requireAdminIdentity: requiredScope?.endsWith(':write'),
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return { userId: request.identity.userId, isAdmin: true };
|
return { userId: auth.userId, isAdmin: auth.isAdmin, token: auth.token };
|
||||||
} catch { /* fall through */ }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.apiToken) {
|
private async requireAdmin(
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
request: { identity?: interfaces.data.IIdentity; apiToken?: string },
|
||||||
if (tokenManager) {
|
scope: interfaces.data.TApiTokenScope = 'gateway-clients:write',
|
||||||
const token = await tokenManager.validateToken(request.apiToken);
|
): Promise<string> {
|
||||||
if (token) {
|
const auth = await requireOpsAuth(this.opsServerRef, request, {
|
||||||
if (!requiredScope || tokenManager.hasScope(token, requiredScope)) {
|
scope,
|
||||||
return { userId: token.createdBy, isAdmin: token.policy?.role === 'admin', token };
|
requireAdminIdentity: true,
|
||||||
}
|
requireAdminToken: true,
|
||||||
throw new plugins.typedrequest.TypedResponseError('insufficient scope');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('unauthorized');
|
|
||||||
}
|
|
||||||
|
|
||||||
private async requireAdmin(request: { identity?: interfaces.data.IIdentity }): Promise<string> {
|
|
||||||
if (request.identity?.jwt) {
|
|
||||||
const isAdmin = await this.opsServerRef.adminHandler.adminIdentityGuard.exec({
|
|
||||||
identity: request.identity,
|
|
||||||
});
|
});
|
||||||
if (isAdmin) return request.identity.userId;
|
return auth.userId;
|
||||||
}
|
|
||||||
throw new plugins.typedrequest.TypedResponseError('admin identity required');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandlers(): void {
|
private registerHandlers(): void {
|
||||||
@@ -83,7 +68,7 @@ export class WorkHosterHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListGatewayClients>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ListGatewayClients>(
|
||||||
'listGatewayClients',
|
'listGatewayClients',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
await this.requireAdmin(dataArg);
|
await this.requireAdmin(dataArg, 'gateway-clients:read');
|
||||||
return { gatewayClients: await this.listManagedGatewayClients() };
|
return { gatewayClients: await this.listManagedGatewayClients() };
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -154,7 +139,7 @@ export class WorkHosterHandler {
|
|||||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateGatewayClientToken>(
|
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateGatewayClientToken>(
|
||||||
'createGatewayClientToken',
|
'createGatewayClientToken',
|
||||||
async (dataArg) => {
|
async (dataArg) => {
|
||||||
const userId = await this.requireAdmin(dataArg);
|
const userId = await this.requireAdmin(dataArg, 'tokens:manage');
|
||||||
const gatewayClient = await this.opsServerRef.dcRouterRef.gatewayClientManager?.getClient(dataArg.gatewayClientId);
|
const gatewayClient = await this.opsServerRef.dcRouterRef.gatewayClientManager?.getClient(dataArg.gatewayClientId);
|
||||||
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
const tokenManager = this.opsServerRef.dcRouterRef.apiTokenManager;
|
||||||
if (!gatewayClient || !gatewayClient.enabled) {
|
if (!gatewayClient || !gatewayClient.enabled) {
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
import type { OpsServer } from '../classes.opsserver.js';
|
||||||
|
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||||
|
|
||||||
|
export interface IAuthRequest {
|
||||||
|
identity?: interfaces.data.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAuthRequirement {
|
||||||
|
scope?: interfaces.data.TApiTokenScope;
|
||||||
|
requireAdminIdentity?: boolean;
|
||||||
|
requireAdminToken?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAuthContext {
|
||||||
|
type: 'identity' | 'apiToken';
|
||||||
|
userId: string;
|
||||||
|
role?: string;
|
||||||
|
isAdmin: boolean;
|
||||||
|
scopes: interfaces.data.TApiTokenScope[];
|
||||||
|
identity?: interfaces.data.IIdentity;
|
||||||
|
token?: interfaces.data.IStoredApiToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typedAuthError = (messageArg: string) => {
|
||||||
|
return new plugins.typedrequest.TypedResponseError(messageArg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function requireOpsAuth(
|
||||||
|
opsServerRefArg: OpsServer,
|
||||||
|
requestArg: IAuthRequest,
|
||||||
|
requirementArg: IAuthRequirement = {},
|
||||||
|
): Promise<IAuthContext> {
|
||||||
|
let identityNeedsAdmin = false;
|
||||||
|
let tokenNeedsAdmin = false;
|
||||||
|
let tokenNeedsScope = false;
|
||||||
|
|
||||||
|
if (requestArg.identity?.jwt) {
|
||||||
|
const identity = await opsServerRefArg.adminHandler.validateIdentity(requestArg.identity);
|
||||||
|
if (identity) {
|
||||||
|
const isAdmin = identity.role === 'admin';
|
||||||
|
if (!requirementArg.requireAdminIdentity || isAdmin) {
|
||||||
|
return {
|
||||||
|
type: 'identity',
|
||||||
|
userId: identity.userId,
|
||||||
|
role: identity.role,
|
||||||
|
isAdmin,
|
||||||
|
scopes: [],
|
||||||
|
identity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
identityNeedsAdmin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestArg.apiToken) {
|
||||||
|
const tokenManager = opsServerRefArg.dcRouterRef.apiTokenManager;
|
||||||
|
const token = tokenManager ? await tokenManager.validateToken(requestArg.apiToken) : null;
|
||||||
|
if (token) {
|
||||||
|
if (requirementArg.requireAdminToken && token.policy?.role !== 'admin') {
|
||||||
|
tokenNeedsAdmin = true;
|
||||||
|
} else if (requirementArg.scope && !tokenManager!.hasScope(token, requirementArg.scope)) {
|
||||||
|
tokenNeedsScope = true;
|
||||||
|
} else {
|
||||||
|
const scopes = token.policy?.role === 'admin'
|
||||||
|
? ['*' as interfaces.data.TApiTokenScope]
|
||||||
|
: Array.from(new Set([...(token.scopes || []), ...(token.policy?.scopes || [])]));
|
||||||
|
return {
|
||||||
|
type: 'apiToken',
|
||||||
|
userId: token.createdBy,
|
||||||
|
role: token.policy?.role || 'operator',
|
||||||
|
isAdmin: token.policy?.role === 'admin',
|
||||||
|
scopes,
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenNeedsScope) {
|
||||||
|
throw typedAuthError('insufficient scope');
|
||||||
|
}
|
||||||
|
if (tokenNeedsAdmin) {
|
||||||
|
throw typedAuthError('admin API token required');
|
||||||
|
}
|
||||||
|
if (identityNeedsAdmin) {
|
||||||
|
throw typedAuthError('admin identity required');
|
||||||
|
}
|
||||||
|
throw typedAuthError('unauthorized');
|
||||||
|
}
|
||||||
@@ -8,22 +8,52 @@ export type IRouteSecurity = NonNullable<IRouteConfig['security']>;
|
|||||||
// Route Management Data Types
|
// Route Management Data Types
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
export type TApiTokenScope =
|
export const apiTokenScopes = [
|
||||||
| '*'
|
'*',
|
||||||
| 'routes:read' | 'routes:write'
|
'routes:read',
|
||||||
| 'config:read'
|
'routes:write',
|
||||||
| 'certificates:read' | 'certificates:write'
|
'config:read',
|
||||||
| 'tokens:read' | 'tokens:manage'
|
'stats:read',
|
||||||
| 'source-profiles:read' | 'source-profiles:write'
|
'logs:read',
|
||||||
| 'target-profiles:read' | 'target-profiles:write'
|
'security:read',
|
||||||
| 'targets:read' | 'targets:write'
|
'security:write',
|
||||||
| 'dns-providers:read' | 'dns-providers:write'
|
'emails:read',
|
||||||
| 'domains:read' | 'domains:write'
|
'emails:write',
|
||||||
| 'dns-records:read' | 'dns-records:write'
|
'certificates:read',
|
||||||
| 'acme-config:read' | 'acme-config:write'
|
'certificates:write',
|
||||||
| 'email-domains:read' | 'email-domains:write'
|
'tokens:read',
|
||||||
| 'gateway-clients:read' | 'gateway-clients:write'
|
'tokens:manage',
|
||||||
| 'workhosters:read' | 'workhosters:write';
|
'users:read',
|
||||||
|
'users:manage',
|
||||||
|
'source-profiles:read',
|
||||||
|
'source-profiles:write',
|
||||||
|
'target-profiles:read',
|
||||||
|
'target-profiles:write',
|
||||||
|
'targets:read',
|
||||||
|
'targets:write',
|
||||||
|
'dns-providers:read',
|
||||||
|
'dns-providers:write',
|
||||||
|
'domains:read',
|
||||||
|
'domains:write',
|
||||||
|
'dns-records:read',
|
||||||
|
'dns-records:write',
|
||||||
|
'acme-config:read',
|
||||||
|
'acme-config:write',
|
||||||
|
'email-domains:read',
|
||||||
|
'email-domains:write',
|
||||||
|
'remote-ingress:read',
|
||||||
|
'remote-ingress:write',
|
||||||
|
'vpn:read',
|
||||||
|
'vpn:write',
|
||||||
|
'radius:read',
|
||||||
|
'radius:write',
|
||||||
|
'gateway-clients:read',
|
||||||
|
'gateway-clients:write',
|
||||||
|
'workhosters:read',
|
||||||
|
'workhosters:write',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type TApiTokenScope = typeof apiTokenScopes[number];
|
||||||
|
|
||||||
export type TGatewayClientType = 'onebox' | 'cloudly' | 'custom';
|
export type TGatewayClientType = 'onebox' | 'cloudly' | 'custom';
|
||||||
/** @deprecated Use TGatewayClientType. */
|
/** @deprecated Use TGatewayClientType. */
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export interface IReq_CreateApiToken extends plugins.typedrequestInterfaces.impl
|
|||||||
> {
|
> {
|
||||||
method: 'createApiToken';
|
method: 'createApiToken';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
name: string;
|
name: string;
|
||||||
scopes: TApiTokenScope[];
|
scopes: TApiTokenScope[];
|
||||||
policy?: IApiTokenPolicy;
|
policy?: IApiTokenPolicy;
|
||||||
@@ -39,7 +40,8 @@ export interface IReq_ListApiTokens extends plugins.typedrequestInterfaces.imple
|
|||||||
> {
|
> {
|
||||||
method: 'listApiTokens';
|
method: 'listApiTokens';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
tokens: IApiTokenInfo[];
|
tokens: IApiTokenInfo[];
|
||||||
@@ -55,7 +57,8 @@ export interface IReq_RevokeApiToken extends plugins.typedrequestInterfaces.impl
|
|||||||
> {
|
> {
|
||||||
method: 'revokeApiToken';
|
method: 'revokeApiToken';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -74,7 +77,8 @@ export interface IReq_RollApiToken extends plugins.typedrequestInterfaces.implem
|
|||||||
> {
|
> {
|
||||||
method: 'rollApiToken';
|
method: 'rollApiToken';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -93,7 +97,8 @@ export interface IReq_ToggleApiToken extends plugins.typedrequestInterfaces.impl
|
|||||||
> {
|
> {
|
||||||
method: 'toggleApiToken';
|
method: 'toggleApiToken';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import type * as data from '../data/index.js';
|
|||||||
export interface IReq_GetCombinedMetrics {
|
export interface IReq_GetCombinedMetrics {
|
||||||
method: 'getCombinedMetrics';
|
method: 'getCombinedMetrics';
|
||||||
request: {
|
request: {
|
||||||
identity: data.IIdentity;
|
identity?: data.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
sections?: {
|
sections?: {
|
||||||
server?: boolean;
|
server?: boolean;
|
||||||
email?: boolean;
|
email?: boolean;
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ export interface IReq_GetConfiguration extends plugins.typedrequestInterfaces.im
|
|||||||
> {
|
> {
|
||||||
method: 'getConfiguration';
|
method: 'getConfiguration';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
section?: string;
|
section?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ export interface IReq_GetAllEmails extends plugins.typedrequestInterfaces.implem
|
|||||||
> {
|
> {
|
||||||
method: 'getAllEmails';
|
method: 'getAllEmails';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
emails: IEmail[];
|
emails: IEmail[];
|
||||||
@@ -84,7 +85,8 @@ export interface IReq_GetEmailDetail extends plugins.typedrequestInterfaces.impl
|
|||||||
> {
|
> {
|
||||||
method: 'getEmailDetail';
|
method: 'getEmailDetail';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
emailId: string;
|
emailId: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -101,7 +103,8 @@ export interface IReq_ResendEmail extends plugins.typedrequestInterfaces.impleme
|
|||||||
> {
|
> {
|
||||||
method: 'resendEmail';
|
method: 'resendEmail';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
emailId: string;
|
emailId: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export interface IReq_GetRecentLogs extends plugins.typedrequestInterfaces.imple
|
|||||||
> {
|
> {
|
||||||
method: 'getRecentLogs';
|
method: 'getRecentLogs';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
level?: 'debug' | 'info' | 'warn' | 'error';
|
level?: 'debug' | 'info' | 'warn' | 'error';
|
||||||
category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
|
category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
|
||||||
limit?: number;
|
limit?: number;
|
||||||
@@ -31,7 +32,8 @@ export interface IReq_GetLogStream extends plugins.typedrequestInterfaces.implem
|
|||||||
> {
|
> {
|
||||||
method: 'getLogStream';
|
method: 'getLogStream';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
follow?: boolean;
|
follow?: boolean;
|
||||||
filters?: {
|
filters?: {
|
||||||
level?: string[];
|
level?: string[];
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export interface IReq_GetRadiusClients extends plugins.typedrequestInterfaces.im
|
|||||||
> {
|
> {
|
||||||
method: 'getRadiusClients';
|
method: 'getRadiusClients';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
clients: Array<{
|
clients: Array<{
|
||||||
@@ -35,7 +36,8 @@ export interface IReq_SetRadiusClient extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'setRadiusClient';
|
method: 'setRadiusClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
client: {
|
client: {
|
||||||
name: string;
|
name: string;
|
||||||
ipRange: string;
|
ipRange: string;
|
||||||
@@ -59,7 +61,8 @@ export interface IReq_RemoveRadiusClient extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'removeRadiusClient';
|
method: 'removeRadiusClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -81,7 +84,8 @@ export interface IReq_GetVlanMappings extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'getVlanMappings';
|
method: 'getVlanMappings';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
mappings: Array<{
|
mappings: Array<{
|
||||||
@@ -108,7 +112,8 @@ export interface IReq_SetVlanMapping extends plugins.typedrequestInterfaces.impl
|
|||||||
> {
|
> {
|
||||||
method: 'setVlanMapping';
|
method: 'setVlanMapping';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
mapping: {
|
mapping: {
|
||||||
mac: string;
|
mac: string;
|
||||||
vlan: number;
|
vlan: number;
|
||||||
@@ -139,7 +144,8 @@ export interface IReq_RemoveVlanMapping extends plugins.typedrequestInterfaces.i
|
|||||||
> {
|
> {
|
||||||
method: 'removeVlanMapping';
|
method: 'removeVlanMapping';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
mac: string;
|
mac: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -157,7 +163,8 @@ export interface IReq_UpdateVlanConfig extends plugins.typedrequestInterfaces.im
|
|||||||
> {
|
> {
|
||||||
method: 'updateVlanConfig';
|
method: 'updateVlanConfig';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
defaultVlan?: number;
|
defaultVlan?: number;
|
||||||
allowUnknownMacs?: boolean;
|
allowUnknownMacs?: boolean;
|
||||||
};
|
};
|
||||||
@@ -179,7 +186,8 @@ export interface IReq_TestVlanAssignment extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'testVlanAssignment';
|
method: 'testVlanAssignment';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
mac: string;
|
mac: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -207,7 +215,8 @@ export interface IReq_GetRadiusSessions extends plugins.typedrequestInterfaces.i
|
|||||||
> {
|
> {
|
||||||
method: 'getRadiusSessions';
|
method: 'getRadiusSessions';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
filter?: {
|
filter?: {
|
||||||
username?: string;
|
username?: string;
|
||||||
nasIpAddress?: string;
|
nasIpAddress?: string;
|
||||||
@@ -243,7 +252,8 @@ export interface IReq_DisconnectRadiusSession extends plugins.typedrequestInterf
|
|||||||
> {
|
> {
|
||||||
method: 'disconnectRadiusSession';
|
method: 'disconnectRadiusSession';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
};
|
};
|
||||||
@@ -262,7 +272,8 @@ export interface IReq_GetRadiusAccountingSummary extends plugins.typedrequestInt
|
|||||||
> {
|
> {
|
||||||
method: 'getRadiusAccountingSummary';
|
method: 'getRadiusAccountingSummary';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
startTime: number;
|
startTime: number;
|
||||||
endTime: number;
|
endTime: number;
|
||||||
};
|
};
|
||||||
@@ -296,7 +307,8 @@ export interface IReq_GetRadiusStatistics extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'getRadiusStatistics';
|
method: 'getRadiusStatistics';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
stats: {
|
stats: {
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ export interface IReq_CreateRemoteIngress extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'createRemoteIngress';
|
method: 'createRemoteIngress';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
name: string;
|
name: string;
|
||||||
listenPorts?: number[];
|
listenPorts?: number[];
|
||||||
autoDerivePorts?: boolean;
|
autoDerivePorts?: boolean;
|
||||||
@@ -36,7 +37,8 @@ export interface IReq_DeleteRemoteIngress extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'deleteRemoteIngress';
|
method: 'deleteRemoteIngress';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -54,7 +56,8 @@ export interface IReq_UpdateRemoteIngress extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'updateRemoteIngress';
|
method: 'updateRemoteIngress';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
listenPorts?: number[];
|
listenPorts?: number[];
|
||||||
@@ -77,7 +80,8 @@ export interface IReq_RegenerateRemoteIngressSecret extends plugins.typedrequest
|
|||||||
> {
|
> {
|
||||||
method: 'regenerateRemoteIngressSecret';
|
method: 'regenerateRemoteIngressSecret';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -95,7 +99,8 @@ export interface IReq_GetRemoteIngresses extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'getRemoteIngresses';
|
method: 'getRemoteIngresses';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
edges: IRemoteIngress[];
|
edges: IRemoteIngress[];
|
||||||
@@ -111,7 +116,8 @@ export interface IReq_GetRemoteIngressStatus extends plugins.typedrequestInterfa
|
|||||||
> {
|
> {
|
||||||
method: 'getRemoteIngressStatus';
|
method: 'getRemoteIngressStatus';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
statuses: IRemoteIngressStatus[];
|
statuses: IRemoteIngressStatus[];
|
||||||
@@ -128,7 +134,8 @@ export interface IReq_GetRemoteIngressConnectionToken extends plugins.typedreque
|
|||||||
> {
|
> {
|
||||||
method: 'getRemoteIngressConnectionToken';
|
method: 'getRemoteIngressConnectionToken';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
edgeId: string;
|
edgeId: string;
|
||||||
hubHost?: string;
|
hubHost?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ export interface IReq_ListSecurityBlockRules extends plugins.typedrequestInterfa
|
|||||||
> {
|
> {
|
||||||
method: 'listSecurityBlockRules';
|
method: 'listSecurityBlockRules';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
rules: ISecurityBlockRule[];
|
rules: ISecurityBlockRule[];
|
||||||
@@ -28,7 +29,8 @@ export interface IReq_CreateSecurityBlockRule extends plugins.typedrequestInterf
|
|||||||
> {
|
> {
|
||||||
method: 'createSecurityBlockRule';
|
method: 'createSecurityBlockRule';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
type: TSecurityBlockRuleType;
|
type: TSecurityBlockRuleType;
|
||||||
value: string;
|
value: string;
|
||||||
matchMode?: TSecurityBlockRuleMatchMode;
|
matchMode?: TSecurityBlockRuleMatchMode;
|
||||||
@@ -48,7 +50,8 @@ export interface IReq_UpdateSecurityBlockRule extends plugins.typedrequestInterf
|
|||||||
> {
|
> {
|
||||||
method: 'updateSecurityBlockRule';
|
method: 'updateSecurityBlockRule';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
matchMode?: TSecurityBlockRuleMatchMode;
|
matchMode?: TSecurityBlockRuleMatchMode;
|
||||||
@@ -68,7 +71,8 @@ export interface IReq_DeleteSecurityBlockRule extends plugins.typedrequestInterf
|
|||||||
> {
|
> {
|
||||||
method: 'deleteSecurityBlockRule';
|
method: 'deleteSecurityBlockRule';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -83,7 +87,8 @@ export interface IReq_ListIpIntelligence extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'listIpIntelligence';
|
method: 'listIpIntelligence';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
records: IIpIntelligenceRecord[];
|
records: IIpIntelligenceRecord[];
|
||||||
@@ -96,7 +101,8 @@ export interface IReq_GetCompiledSecurityPolicy extends plugins.typedrequestInte
|
|||||||
> {
|
> {
|
||||||
method: 'getCompiledSecurityPolicy';
|
method: 'getCompiledSecurityPolicy';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
policy: ISecurityCompiledPolicy;
|
policy: ISecurityCompiledPolicy;
|
||||||
@@ -109,7 +115,8 @@ export interface IReq_ListSecurityPolicyAudit extends plugins.typedrequestInterf
|
|||||||
> {
|
> {
|
||||||
method: 'listSecurityPolicyAudit';
|
method: 'listSecurityPolicyAudit';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -123,7 +130,8 @@ export interface IReq_RefreshIpIntelligence extends plugins.typedrequestInterfac
|
|||||||
> {
|
> {
|
||||||
method: 'refreshIpIntelligence';
|
method: 'refreshIpIntelligence';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
ipAddress: string;
|
ipAddress: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export interface IReq_GetServerStatistics extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'getServerStatistics';
|
method: 'getServerStatistics';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
includeHistory?: boolean;
|
includeHistory?: boolean;
|
||||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||||
};
|
};
|
||||||
@@ -29,7 +30,8 @@ export interface IReq_GetEmailStatistics extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'getEmailStatistics';
|
method: 'getEmailStatistics';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||||
domain?: string;
|
domain?: string;
|
||||||
includeDetails?: boolean;
|
includeDetails?: boolean;
|
||||||
@@ -49,7 +51,8 @@ export interface IReq_GetDnsStatistics extends plugins.typedrequestInterfaces.im
|
|||||||
> {
|
> {
|
||||||
method: 'getDnsStatistics';
|
method: 'getDnsStatistics';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||||
domain?: string;
|
domain?: string;
|
||||||
includeQueryTypes?: boolean;
|
includeQueryTypes?: boolean;
|
||||||
@@ -69,7 +72,8 @@ export interface IReq_GetRateLimitStatus extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'getRateLimitStatus';
|
method: 'getRateLimitStatus';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
domain?: string;
|
domain?: string;
|
||||||
ip?: string;
|
ip?: string;
|
||||||
includeBlocked?: boolean;
|
includeBlocked?: boolean;
|
||||||
@@ -91,7 +95,8 @@ export interface IReq_GetSecurityMetrics extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'getSecurityMetrics';
|
method: 'getSecurityMetrics';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||||
includeDetails?: boolean;
|
includeDetails?: boolean;
|
||||||
};
|
};
|
||||||
@@ -112,7 +117,8 @@ export interface IReq_GetActiveConnections extends plugins.typedrequestInterface
|
|||||||
> {
|
> {
|
||||||
method: 'getActiveConnections';
|
method: 'getActiveConnections';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
protocol?: 'smtp' | 'smtps' | 'http' | 'https';
|
protocol?: 'smtp' | 'smtps' | 'http' | 'https';
|
||||||
state?: string;
|
state?: string;
|
||||||
};
|
};
|
||||||
@@ -137,7 +143,8 @@ export interface IReq_GetQueueStatus extends plugins.typedrequestInterfaces.impl
|
|||||||
> {
|
> {
|
||||||
method: 'getQueueStatus';
|
method: 'getQueueStatus';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
queueName?: string;
|
queueName?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -153,7 +160,8 @@ export interface IReq_GetHealthStatus extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'getHealthStatus';
|
method: 'getHealthStatus';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
detailed?: boolean;
|
detailed?: boolean;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -168,7 +176,8 @@ export interface IReq_GetNetworkStats extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'getNetworkStats';
|
method: 'getNetworkStats';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
connectionsByIP: Array<{ ip: string; count: number }>;
|
connectionsByIP: Array<{ ip: string; count: number }>;
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export interface IReq_ListUsers extends plugins.typedrequestInterfaces.implement
|
|||||||
> {
|
> {
|
||||||
method: 'listUsers';
|
method: 'listUsers';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
users: IAdminUserProjection[];
|
users: IAdminUserProjection[];
|
||||||
@@ -30,7 +31,8 @@ export interface IReq_CreateUser extends plugins.typedrequestInterfaces.implemen
|
|||||||
> {
|
> {
|
||||||
method: 'createUser';
|
method: 'createUser';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
email: string;
|
email: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
role: TUserManagementRole;
|
role: TUserManagementRole;
|
||||||
@@ -53,7 +55,8 @@ export interface IReq_DeleteUser extends plugins.typedrequestInterfaces.implemen
|
|||||||
> {
|
> {
|
||||||
method: 'deleteUser';
|
method: 'deleteUser';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ export interface IReq_GetVpnClients extends plugins.typedrequestInterfaces.imple
|
|||||||
> {
|
> {
|
||||||
method: 'getVpnClients';
|
method: 'getVpnClients';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
clients: IVpnClient[];
|
clients: IVpnClient[];
|
||||||
@@ -31,7 +32,8 @@ export interface IReq_GetVpnStatus extends plugins.typedrequestInterfaces.implem
|
|||||||
> {
|
> {
|
||||||
method: 'getVpnStatus';
|
method: 'getVpnStatus';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
status: IVpnServerStatus;
|
status: IVpnServerStatus;
|
||||||
@@ -47,7 +49,8 @@ export interface IReq_CreateVpnClient extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'createVpnClient';
|
method: 'createVpnClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
targetProfileIds?: string[];
|
targetProfileIds?: string[];
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -78,7 +81,8 @@ export interface IReq_UpdateVpnClient extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'updateVpnClient';
|
method: 'updateVpnClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
targetProfileIds?: string[];
|
targetProfileIds?: string[];
|
||||||
@@ -106,7 +110,8 @@ export interface IReq_GetVpnConnectedClients extends plugins.typedrequestInterfa
|
|||||||
> {
|
> {
|
||||||
method: 'getVpnConnectedClients';
|
method: 'getVpnConnectedClients';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
connectedClients: IVpnConnectedClient[];
|
connectedClients: IVpnConnectedClient[];
|
||||||
@@ -122,7 +127,8 @@ export interface IReq_DeleteVpnClient extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'deleteVpnClient';
|
method: 'deleteVpnClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -140,7 +146,8 @@ export interface IReq_EnableVpnClient extends plugins.typedrequestInterfaces.imp
|
|||||||
> {
|
> {
|
||||||
method: 'enableVpnClient';
|
method: 'enableVpnClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -158,7 +165,8 @@ export interface IReq_DisableVpnClient extends plugins.typedrequestInterfaces.im
|
|||||||
> {
|
> {
|
||||||
method: 'disableVpnClient';
|
method: 'disableVpnClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -176,7 +184,8 @@ export interface IReq_RotateVpnClientKey extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'rotateVpnClientKey';
|
method: 'rotateVpnClientKey';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -196,7 +205,8 @@ export interface IReq_ExportVpnClientConfig extends plugins.typedrequestInterfac
|
|||||||
> {
|
> {
|
||||||
method: 'exportVpnClientConfig';
|
method: 'exportVpnClientConfig';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
format: 'smartvpn' | 'wireguard';
|
format: 'smartvpn' | 'wireguard';
|
||||||
};
|
};
|
||||||
@@ -216,7 +226,8 @@ export interface IReq_GetVpnClientTelemetry extends plugins.typedrequestInterfac
|
|||||||
> {
|
> {
|
||||||
method: 'getVpnClientTelemetry';
|
method: 'getVpnClientTelemetry';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ export interface IReq_ListGatewayClients extends plugins.typedrequestInterfaces.
|
|||||||
> {
|
> {
|
||||||
method: 'listGatewayClients';
|
method: 'listGatewayClients';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
gatewayClients: IGatewayClient[];
|
gatewayClients: IGatewayClient[];
|
||||||
@@ -66,7 +67,8 @@ export interface IReq_CreateGatewayClient extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'createGatewayClient';
|
method: 'createGatewayClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
type: IGatewayClient['type'];
|
type: IGatewayClient['type'];
|
||||||
name: string;
|
name: string;
|
||||||
@@ -88,7 +90,8 @@ export interface IReq_UpdateGatewayClient extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'updateGatewayClient';
|
method: 'updateGatewayClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -110,7 +113,8 @@ export interface IReq_DeleteGatewayClient extends plugins.typedrequestInterfaces
|
|||||||
> {
|
> {
|
||||||
method: 'deleteGatewayClient';
|
method: 'deleteGatewayClient';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
@@ -125,7 +129,8 @@ export interface IReq_CreateGatewayClientToken extends plugins.typedrequestInter
|
|||||||
> {
|
> {
|
||||||
method: 'createGatewayClientToken';
|
method: 'createGatewayClientToken';
|
||||||
request: {
|
request: {
|
||||||
identity: authInterfaces.IIdentity;
|
identity?: authInterfaces.IIdentity;
|
||||||
|
apiToken?: string;
|
||||||
gatewayClientId: string;
|
gatewayClientId: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
expiresInDays?: number | null;
|
expiresInDays?: number | null;
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ export async function createMigrationRunner(
|
|||||||
db: db as any,
|
db: db as any,
|
||||||
// Brand-new installs skip all migrations and stamp directly to the current version.
|
// Brand-new installs skip all migrations and stamp directly to the current version.
|
||||||
freshInstallVersion: targetVersion,
|
freshInstallVersion: targetVersion,
|
||||||
|
// dcrouter uses the package version as targetVersion; bridge releases without DB changes.
|
||||||
|
targetVersionStrategy: 'bridge',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register steps in execution order. Each step's .from() must match the
|
// Register steps in execution order. Each step's .from() must match the
|
||||||
|
|||||||
@@ -200,26 +200,7 @@ export class OpsViewApiTokens extends DeesElement {
|
|||||||
private async showCreateTokenDialog() {
|
private async showCreateTokenDialog() {
|
||||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||||
|
|
||||||
const allScopes = [
|
const allScopes = [...interfaces.data.apiTokenScopes];
|
||||||
'*',
|
|
||||||
'routes:read',
|
|
||||||
'routes:write',
|
|
||||||
'config:read',
|
|
||||||
'certificates:read',
|
|
||||||
'certificates:write',
|
|
||||||
'tokens:read',
|
|
||||||
'tokens:manage',
|
|
||||||
'domains:read',
|
|
||||||
'domains:write',
|
|
||||||
'dns-records:read',
|
|
||||||
'dns-records:write',
|
|
||||||
'email-domains:read',
|
|
||||||
'email-domains:write',
|
|
||||||
'gateway-clients:read',
|
|
||||||
'gateway-clients:write',
|
|
||||||
'workhosters:read',
|
|
||||||
'workhosters:write',
|
|
||||||
];
|
|
||||||
|
|
||||||
await DeesModal.createAndShow({
|
await DeesModal.createAndShow({
|
||||||
heading: 'Create API Token',
|
heading: 'Create API Token',
|
||||||
|
|||||||
Reference in New Issue
Block a user