mythik-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +4 -0
- package/README.md +51 -0
- package/dist/api-handler.d.ts +4 -0
- package/dist/api-handler.d.ts.map +1 -0
- package/dist/api-handler.js +110 -0
- package/dist/api-handler.js.map +1 -0
- package/dist/audit.d.ts +18 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +42 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/db-auth-provider.d.ts +10 -0
- package/dist/auth/db-auth-provider.d.ts.map +1 -0
- package/dist/auth/db-auth-provider.js +130 -0
- package/dist/auth/db-auth-provider.js.map +1 -0
- package/dist/auth/jwt-strategy.d.ts +3 -0
- package/dist/auth/jwt-strategy.d.ts.map +1 -0
- package/dist/auth/jwt-strategy.js +39 -0
- package/dist/auth/jwt-strategy.js.map +1 -0
- package/dist/auth/middleware.d.ts +4 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +40 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/password-verifier.d.ts +3 -0
- package/dist/auth/password-verifier.d.ts.map +1 -0
- package/dist/auth/password-verifier.js +18 -0
- package/dist/auth/password-verifier.js.map +1 -0
- package/dist/auth/policy-evaluator.d.ts +3 -0
- package/dist/auth/policy-evaluator.d.ts.map +1 -0
- package/dist/auth/policy-evaluator.js +11 -0
- package/dist/auth/policy-evaluator.js.map +1 -0
- package/dist/auth/refresh-store.d.ts +8 -0
- package/dist/auth/refresh-store.d.ts.map +1 -0
- package/dist/auth/refresh-store.js +34 -0
- package/dist/auth/refresh-store.js.map +1 -0
- package/dist/auth/scope-filter.d.ts +10 -0
- package/dist/auth/scope-filter.d.ts.map +1 -0
- package/dist/auth/scope-filter.js +51 -0
- package/dist/auth/scope-filter.js.map +1 -0
- package/dist/auth/types.d.ts +75 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +3 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/user-context.d.ts +4 -0
- package/dist/auth/user-context.d.ts.map +1 -0
- package/dist/auth/user-context.js +18 -0
- package/dist/auth/user-context.js.map +1 -0
- package/dist/catalog-builder.d.ts +3 -0
- package/dist/catalog-builder.d.ts.map +1 -0
- package/dist/catalog-builder.js +35 -0
- package/dist/catalog-builder.js.map +1 -0
- package/dist/connection.d.ts +4 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +18 -0
- package/dist/connection.js.map +1 -0
- package/dist/crud-builder.d.ts +14 -0
- package/dist/crud-builder.d.ts.map +1 -0
- package/dist/crud-builder.js +43 -0
- package/dist/crud-builder.js.map +1 -0
- package/dist/handler-loader.d.ts +9 -0
- package/dist/handler-loader.d.ts.map +1 -0
- package/dist/handler-loader.js +42 -0
- package/dist/handler-loader.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/cors.d.ts +3 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +8 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +9 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +56 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/query-engine.d.ts +8 -0
- package/dist/query-engine.d.ts.map +1 -0
- package/dist/query-engine.js +66 -0
- package/dist/query-engine.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +717 -0
- package/dist/server.js.map +1 -0
- package/dist/spec-loader.d.ts +6 -0
- package/dist/spec-loader.d.ts.map +1 -0
- package/dist/spec-loader.js +21 -0
- package/dist/spec-loader.js.map +1 -0
- package/dist/spec-serving.d.ts +9 -0
- package/dist/spec-serving.d.ts.map +1 -0
- package/dist/spec-serving.js +53 -0
- package/dist/spec-serving.js.map +1 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validation/identifier-guard.d.ts +2 -0
- package/dist/validation/identifier-guard.d.ts.map +1 -0
- package/dist/validation/identifier-guard.js +3 -0
- package/dist/validation/identifier-guard.js.map +1 -0
- package/dist/validation/spec-validator.d.ts +3 -0
- package/dist/validation/spec-validator.d.ts.map +1 -0
- package/dist/validation/spec-validator.js +3 -0
- package/dist/validation/spec-validator.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
export function createJwtStrategy(config) {
|
|
3
|
+
const secret = config.secret;
|
|
4
|
+
function extractToken(req) {
|
|
5
|
+
const auth = req.headers.authorization ?? req.headers.Authorization;
|
|
6
|
+
if (typeof auth !== 'string')
|
|
7
|
+
return null;
|
|
8
|
+
if (!auth.startsWith('Bearer '))
|
|
9
|
+
return null;
|
|
10
|
+
const token = auth.slice(7).trim();
|
|
11
|
+
return token.length > 0 ? token : null;
|
|
12
|
+
}
|
|
13
|
+
async function validateToken(token) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const options = {};
|
|
16
|
+
if (config.issuer)
|
|
17
|
+
options.issuer = config.issuer;
|
|
18
|
+
if (config.audience)
|
|
19
|
+
options.audience = config.audience;
|
|
20
|
+
jwt.verify(token, secret, options, (err, decoded) => {
|
|
21
|
+
if (err)
|
|
22
|
+
return reject(err);
|
|
23
|
+
resolve(decoded);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function generateToken(payload, expiresInMinutes) {
|
|
28
|
+
const options = {
|
|
29
|
+
expiresIn: expiresInMinutes * 60,
|
|
30
|
+
};
|
|
31
|
+
if (config.issuer)
|
|
32
|
+
options.issuer = config.issuer;
|
|
33
|
+
if (config.audience)
|
|
34
|
+
options.audience = config.audience;
|
|
35
|
+
return jwt.sign(payload, secret, options);
|
|
36
|
+
}
|
|
37
|
+
return { extractToken, validateToken, generateToken };
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=jwt-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-strategy.js","sourceRoot":"","sources":["../../src/auth/jwt-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAG/B,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAE7B,SAAS,YAAY,CAAC,GAA+D;QACnF,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;QACpE,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;IAED,KAAK,UAAU,aAAa,CAAC,KAAa;QACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAsB,EAAE,CAAC;YACtC,IAAI,MAAM,CAAC,MAAM;gBAAE,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAClD,IAAI,MAAM,CAAC,QAAQ;gBAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YAExD,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;gBAClD,IAAI,GAAG;oBAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC5B,OAAO,CAAC,OAAkC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,aAAa,CAAC,OAAgC,EAAE,gBAAwB;QAC/E,MAAM,OAAO,GAAoB;YAC/B,SAAS,EAAE,gBAAgB,GAAG,EAAE;SACjC,CAAC;QACF,IAAI,MAAM,CAAC,MAAM;YAAE,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAClD,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAExD,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import type { AuthConfig } from './types.js';
|
|
3
|
+
export declare function createAuthMiddleware(authConfig: AuthConfig): (policy?: string) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
4
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,IAKnB,SAAS,MAAM,MACd,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAiCvG"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createJwtStrategy } from './jwt-strategy.js';
|
|
2
|
+
import { buildUserContext } from './user-context.js';
|
|
3
|
+
import { evaluatePolicy } from './policy-evaluator.js';
|
|
4
|
+
export function createAuthMiddleware(authConfig) {
|
|
5
|
+
const jwtStrategy = createJwtStrategy(authConfig.jwt);
|
|
6
|
+
const policies = authConfig.policies ?? {};
|
|
7
|
+
const claimsMapping = authConfig.jwt.claims ?? {};
|
|
8
|
+
return function authMiddlewareFactory(policy) {
|
|
9
|
+
return async function authMiddleware(req, res, next) {
|
|
10
|
+
// Public endpoints skip auth
|
|
11
|
+
if (policy === 'public')
|
|
12
|
+
return next();
|
|
13
|
+
// Extract token
|
|
14
|
+
const token = jwtStrategy.extractToken(req);
|
|
15
|
+
if (!token) {
|
|
16
|
+
res.status(401).json({ error: { code: 'TOKEN_REQUIRED', message: 'Authentication required' } });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Validate token
|
|
20
|
+
let claims;
|
|
21
|
+
try {
|
|
22
|
+
claims = await jwtStrategy.validateToken(token);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
res.status(401).json({ error: { code: 'TOKEN_INVALID', message: 'Invalid or expired token' } });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Build user context
|
|
29
|
+
const user = buildUserContext(claims, claimsMapping);
|
|
30
|
+
req.user = user;
|
|
31
|
+
// Evaluate policy
|
|
32
|
+
if (!evaluatePolicy(policy, user.roles, policies)) {
|
|
33
|
+
res.status(403).json({ error: { code: 'FORBIDDEN', message: 'Insufficient permissions' } });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
next();
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,UAAU,oBAAoB,CAAC,UAAsB;IACzD,MAAM,WAAW,GAAG,iBAAiB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;IAC3C,MAAM,aAAa,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;IAElD,OAAO,SAAS,qBAAqB,CAAC,MAAe;QACnD,OAAO,KAAK,UAAU,cAAc,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;YAClF,6BAA6B;YAC7B,IAAI,MAAM,KAAK,QAAQ;gBAAE,OAAO,IAAI,EAAE,CAAC;YAEvC,gBAAgB;YAChB,MAAM,KAAK,GAAG,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE,CAAC,CAAC;gBAChG,OAAO;YACT,CAAC;YAED,iBAAiB;YACjB,IAAI,MAA+B,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAClD,CAAC;YAAC,MAAM,CAAC;gBACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAC;gBAChG,OAAO;YACT,CAAC;YAED,qBAAqB;YACrB,MAAM,IAAI,GAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;YACjE,GAA0C,CAAC,IAAI,GAAG,IAAI,CAAC;YAExD,kBAAkB;YAClB,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;gBAClD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC,CAAC;gBAC5F,OAAO;YACT,CAAC;YAED,IAAI,EAAE,CAAC;QACT,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-verifier.d.ts","sourceRoot":"","sources":["../../src/auth/password-verifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAYnD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAOnF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import bcrypt from 'bcryptjs';
|
|
2
|
+
const bcryptVerifier = {
|
|
3
|
+
async verify(password, hash) {
|
|
4
|
+
return bcrypt.compare(password, hash);
|
|
5
|
+
},
|
|
6
|
+
};
|
|
7
|
+
const verifiers = {
|
|
8
|
+
bcrypt: bcryptVerifier,
|
|
9
|
+
};
|
|
10
|
+
export function getPasswordVerifier(algorithm) {
|
|
11
|
+
const name = algorithm ?? 'bcrypt';
|
|
12
|
+
const verifier = verifiers[name];
|
|
13
|
+
if (!verifier) {
|
|
14
|
+
throw new Error(`Unsupported password hash algorithm: "${name}". Supported: ${Object.keys(verifiers).join(', ')}`);
|
|
15
|
+
}
|
|
16
|
+
return verifier;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=password-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-verifier.js","sourceRoot":"","sources":["../../src/auth/password-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,UAAU,CAAC;AAG9B,MAAM,cAAc,GAAqB;IACvC,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,IAAY;QACzC,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC;CACF,CAAC;AAEF,MAAM,SAAS,GAAqC;IAClD,MAAM,EAAE,cAAc;CACvB,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,SAA6B;IAC/D,MAAM,IAAI,GAAG,SAAS,IAAI,QAAQ,CAAC;IACnC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-evaluator.d.ts","sourceRoot":"","sources":["../../src/auth/policy-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACrC,OAAO,CAQT"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function evaluatePolicy(policy, userRoles, policies) {
|
|
2
|
+
if (policy === 'public')
|
|
3
|
+
return true;
|
|
4
|
+
if (!policy || policy === 'authenticated')
|
|
5
|
+
return true;
|
|
6
|
+
const policyConfig = policies[policy];
|
|
7
|
+
if (!policyConfig)
|
|
8
|
+
return false;
|
|
9
|
+
return policyConfig.roles.some(role => userRoles.includes(role));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=policy-evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-evaluator.js","sourceRoot":"","sources":["../../src/auth/policy-evaluator.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAC5B,MAA0B,EAC1B,SAAmB,EACnB,QAAsC;IAEtC,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,eAAe;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface RefreshStore {
|
|
2
|
+
store(username: string, token: string): void;
|
|
3
|
+
getUsername(token: string): string | null;
|
|
4
|
+
revoke(token: string): void;
|
|
5
|
+
generateToken(): string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createRefreshStore(ttlMinutes: number): RefreshStore;
|
|
8
|
+
//# sourceMappingURL=refresh-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-store.d.ts","sourceRoot":"","sources":["../../src/auth/refresh-store.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,IAAI,MAAM,CAAC;CACzB;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAmCnE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
export function createRefreshStore(ttlMinutes) {
|
|
3
|
+
const tokens = new Map();
|
|
4
|
+
const ttlMs = ttlMinutes * 60 * 1000;
|
|
5
|
+
function cleanExpired() {
|
|
6
|
+
const now = Date.now();
|
|
7
|
+
for (const [token, entry] of tokens) {
|
|
8
|
+
if (entry.expiry <= now)
|
|
9
|
+
tokens.delete(token);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function store(username, token) {
|
|
13
|
+
tokens.set(token, { username, expiry: Date.now() + ttlMs });
|
|
14
|
+
cleanExpired();
|
|
15
|
+
}
|
|
16
|
+
function getUsername(token) {
|
|
17
|
+
const entry = tokens.get(token);
|
|
18
|
+
if (!entry)
|
|
19
|
+
return null;
|
|
20
|
+
if (entry.expiry <= Date.now()) {
|
|
21
|
+
tokens.delete(token);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return entry.username;
|
|
25
|
+
}
|
|
26
|
+
function revoke(token) {
|
|
27
|
+
tokens.delete(token);
|
|
28
|
+
}
|
|
29
|
+
function generateToken() {
|
|
30
|
+
return randomBytes(64).toString('base64url');
|
|
31
|
+
}
|
|
32
|
+
return { store, getUsername, revoke, generateToken };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=refresh-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-store.js","sourceRoot":"","sources":["../../src/auth/refresh-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAcrC,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,KAAK,GAAG,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC;IAErC,SAAS,YAAY;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;gBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,SAAS,KAAK,CAAC,QAAgB,EAAE,KAAa;QAC5C,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QAC5D,YAAY,EAAE,CAAC;IACjB,CAAC;IAED,SAAS,WAAW,CAAC,KAAa;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,QAAQ,CAAC;IACxB,CAAC;IAED,SAAS,MAAM,CAAC,KAAa;QAC3B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IAED,SAAS,aAAa;QACpB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ScopeFilterConfig } from './types.js';
|
|
2
|
+
export interface ScopeClause {
|
|
3
|
+
sql: string;
|
|
4
|
+
params: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveActiveScope(headerValue: string | undefined, type: 'int' | 'string' | undefined): unknown | null;
|
|
7
|
+
export declare function buildScopeWhereClause(config: ScopeFilterConfig, userScope: unknown[], activeScope: unknown | undefined, userRoles: string[], columnOverride?: string): ScopeClause | null;
|
|
8
|
+
export declare function validateScopeForInsert(config: ScopeFilterConfig, body: Record<string, unknown>, userScope: unknown[], userRoles: string[]): boolean;
|
|
9
|
+
export declare function wrapQueryWithScopeFilter(originalSql: string, scopeClause: ScopeClause): string;
|
|
10
|
+
//# sourceMappingURL=scope-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GACjC,OAAO,GAAG,IAAI,CAKhB;AAMD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,OAAO,EAAE,EACpB,WAAW,EAAE,OAAO,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EAAE,EACnB,cAAc,CAAC,EAAE,MAAM,GACtB,WAAW,GAAG,IAAI,CA4BpB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,OAAO,EAAE,EACpB,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAST;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,GACvB,MAAM,CAER"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { assertValidIdentifier } from 'mythik';
|
|
2
|
+
export function resolveActiveScope(headerValue, type) {
|
|
3
|
+
if (headerValue === undefined || headerValue === '')
|
|
4
|
+
return null;
|
|
5
|
+
if (type === 'string')
|
|
6
|
+
return headerValue;
|
|
7
|
+
const parsed = parseInt(headerValue, 10);
|
|
8
|
+
return isNaN(parsed) ? null : parsed;
|
|
9
|
+
}
|
|
10
|
+
function hasBypassRole(bypassRoles, userRoles) {
|
|
11
|
+
return bypassRoles.some(role => userRoles.includes(role));
|
|
12
|
+
}
|
|
13
|
+
export function buildScopeWhereClause(config, userScope, activeScope, userRoles, columnOverride) {
|
|
14
|
+
const bypassRoles = config.bypassRoles ?? [];
|
|
15
|
+
if (hasBypassRole(bypassRoles, userRoles))
|
|
16
|
+
return null;
|
|
17
|
+
const column = columnOverride ?? config.column;
|
|
18
|
+
assertValidIdentifier(column, 'scopeFilter.column');
|
|
19
|
+
const mode = config.mode ?? 'all';
|
|
20
|
+
if (mode === 'select') {
|
|
21
|
+
return {
|
|
22
|
+
sql: `_scoped.${column} = @_activeScope`,
|
|
23
|
+
params: { _activeScope: activeScope },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// mode "all"
|
|
27
|
+
if (userScope.length === 0) {
|
|
28
|
+
return { sql: '1 = 0', params: {} };
|
|
29
|
+
}
|
|
30
|
+
const paramNames = userScope.map((_, i) => `@_scope${i}`);
|
|
31
|
+
const params = {};
|
|
32
|
+
userScope.forEach((val, i) => { params[`_scope${i}`] = val; });
|
|
33
|
+
return {
|
|
34
|
+
sql: `_scoped.${column} IN (${paramNames.join(', ')})`,
|
|
35
|
+
params,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function validateScopeForInsert(config, body, userScope, userRoles) {
|
|
39
|
+
const bypassRoles = config.bypassRoles ?? [];
|
|
40
|
+
if (hasBypassRole(bypassRoles, userRoles))
|
|
41
|
+
return true;
|
|
42
|
+
const column = config.column;
|
|
43
|
+
const bodyValue = body[column];
|
|
44
|
+
if (bodyValue === undefined || bodyValue === null)
|
|
45
|
+
return false;
|
|
46
|
+
return userScope.includes(bodyValue);
|
|
47
|
+
}
|
|
48
|
+
export function wrapQueryWithScopeFilter(originalSql, scopeClause) {
|
|
49
|
+
return `SELECT * FROM (\n${originalSql}\n) AS _scoped\nWHERE ${scopeClause.sql}`;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=scope-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAO/C,MAAM,UAAU,kBAAkB,CAChC,WAA+B,EAC/B,IAAkC;IAElC,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACjE,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,WAAW,CAAC;IAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AACvC,CAAC;AAED,SAAS,aAAa,CAAC,WAAqB,EAAE,SAAmB;IAC/D,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,MAAyB,EACzB,SAAoB,EACpB,WAAgC,EAChC,SAAmB,EACnB,cAAuB;IAEvB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IAC7C,IAAI,aAAa,CAAC,WAAW,EAAE,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,MAAM,GAAG,cAAc,IAAI,MAAM,CAAC,MAAM,CAAC;IAC/C,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC;IAElC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;YACL,GAAG,EAAE,WAAW,MAAM,kBAAkB;YACxC,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE;SACtC,CAAC;IACJ,CAAC;IAED,aAAa;IACb,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACtC,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC1D,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/D,OAAO;QACL,GAAG,EAAE,WAAW,MAAM,QAAQ,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACtD,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAyB,EACzB,IAA6B,EAC7B,SAAoB,EACpB,SAAmB;IAEnB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IAC7C,IAAI,aAAa,CAAC,WAAW,EAAE,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAEhE,OAAO,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,WAAmB,EACnB,WAAwB;IAExB,OAAO,oBAAoB,WAAW,yBAAyB,WAAW,CAAC,GAAG,EAAE,CAAC;AACnF,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export interface AuthStrategy {
|
|
2
|
+
extractToken(req: {
|
|
3
|
+
headers: Record<string, string | string[] | undefined>;
|
|
4
|
+
}): string | null;
|
|
5
|
+
validateToken(token: string): Promise<Record<string, unknown>>;
|
|
6
|
+
generateToken(payload: Record<string, unknown>, expiresInMinutes: number): string;
|
|
7
|
+
}
|
|
8
|
+
export interface PasswordVerifier {
|
|
9
|
+
verify(password: string, hash: string): Promise<boolean>;
|
|
10
|
+
}
|
|
11
|
+
export interface JwtConfig {
|
|
12
|
+
secret: string;
|
|
13
|
+
issuer?: string;
|
|
14
|
+
audience?: string;
|
|
15
|
+
expiresIn?: number;
|
|
16
|
+
refreshExpiresIn?: number;
|
|
17
|
+
claims?: ClaimsMapping;
|
|
18
|
+
}
|
|
19
|
+
export interface ClaimsMapping {
|
|
20
|
+
username?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
roles?: string;
|
|
23
|
+
scope?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ProviderConfig {
|
|
26
|
+
usersTable: string;
|
|
27
|
+
usernameColumn: string;
|
|
28
|
+
passwordColumn: string;
|
|
29
|
+
passwordHash?: 'bcrypt' | 'argon2';
|
|
30
|
+
activeCondition?: string;
|
|
31
|
+
emailColumn?: string;
|
|
32
|
+
displayNameQuery?: string;
|
|
33
|
+
rolesQuery: string;
|
|
34
|
+
scopeQuery: string;
|
|
35
|
+
defaultScopeQuery?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface PolicyConfig {
|
|
38
|
+
roles: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface ScopeFilterConfig {
|
|
41
|
+
claim: string;
|
|
42
|
+
type?: 'int' | 'string';
|
|
43
|
+
column: string;
|
|
44
|
+
bypassRoles?: string[];
|
|
45
|
+
mode?: 'all' | 'select';
|
|
46
|
+
header?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface EndpointScopeOverride {
|
|
49
|
+
column: string;
|
|
50
|
+
}
|
|
51
|
+
export interface AuthConfig {
|
|
52
|
+
strategy: 'jwt';
|
|
53
|
+
jwt: JwtConfig;
|
|
54
|
+
provider?: ProviderConfig;
|
|
55
|
+
policies?: Record<string, PolicyConfig>;
|
|
56
|
+
scopeFilter?: ScopeFilterConfig;
|
|
57
|
+
catalogsPolicy?: 'public';
|
|
58
|
+
}
|
|
59
|
+
export interface LoginResponseUser {
|
|
60
|
+
id: string;
|
|
61
|
+
email: string;
|
|
62
|
+
name: string;
|
|
63
|
+
role: string;
|
|
64
|
+
roles: string[];
|
|
65
|
+
scope: unknown[];
|
|
66
|
+
defaultScope: unknown;
|
|
67
|
+
metadata: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
export interface LoginResponse {
|
|
70
|
+
token: string;
|
|
71
|
+
refreshToken: string;
|
|
72
|
+
expiresIn: number;
|
|
73
|
+
user: LoginResponseUser;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;KAAE,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7F,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/D,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;CACnF;AAID,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1D;AAID,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAID,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAID,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,KAAK,CAAC;IAChB,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAID,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,iBAAiB,CAAC;CACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,4DAA4D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-context.d.ts","sourceRoot":"","sources":["../../src/auth/user-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,CAoBrG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function buildUserContext(claims, mapping) {
|
|
2
|
+
const usernameKey = mapping.username ?? 'sub';
|
|
3
|
+
const nameKey = mapping.name;
|
|
4
|
+
const rolesKey = mapping.roles ?? 'roles';
|
|
5
|
+
const scopeKey = mapping.scope;
|
|
6
|
+
const username = String(claims[usernameKey] ?? '');
|
|
7
|
+
const name = nameKey ? claims[nameKey] ?? null : null;
|
|
8
|
+
const rolesRaw = claims[rolesKey];
|
|
9
|
+
const roles = Array.isArray(rolesRaw)
|
|
10
|
+
? rolesRaw
|
|
11
|
+
: typeof rolesRaw === 'string'
|
|
12
|
+
? [rolesRaw]
|
|
13
|
+
: [];
|
|
14
|
+
const scopeRaw = scopeKey ? claims[scopeKey] : undefined;
|
|
15
|
+
const scope = Array.isArray(scopeRaw) ? scopeRaw : [];
|
|
16
|
+
return { username, name, roles, scope, raw: claims };
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=user-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-context.js","sourceRoot":"","sources":["../../src/auth/user-context.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,gBAAgB,CAAC,MAA+B,EAAE,OAAsB;IACtF,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC;IAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC;IAE/B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAE,MAAM,CAAC,OAAO,CAAmB,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAEzE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,KAAK,GAAa,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC7C,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ;YAC5B,CAAC,CAAC,CAAC,QAAQ,CAAC;YACZ,CAAC,CAAC,EAAE,CAAC;IAET,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,MAAM,KAAK,GAAc,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAEjE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-builder.d.ts","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAiCtE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { assertValidIdentifier } from './validation/identifier-guard.js';
|
|
2
|
+
export function buildCatalogQuery(config) {
|
|
3
|
+
if (config.static)
|
|
4
|
+
return null;
|
|
5
|
+
if (!config.from)
|
|
6
|
+
return null;
|
|
7
|
+
assertValidIdentifier(config.from, 'catalog.from');
|
|
8
|
+
if (config.distinct) {
|
|
9
|
+
assertValidIdentifier(config.distinct, 'catalog.distinct');
|
|
10
|
+
const orderByClause = config.orderBy
|
|
11
|
+
? `ORDER BY ${bracketOrderBy(config.orderBy)}`
|
|
12
|
+
: `ORDER BY [${config.distinct}] ASC`;
|
|
13
|
+
return `SELECT DISTINCT [${config.distinct}] AS [value], [${config.distinct}] AS [label] FROM [${config.from}] ${orderByClause}`;
|
|
14
|
+
}
|
|
15
|
+
assertValidIdentifier(config.value, 'catalog.value');
|
|
16
|
+
assertValidIdentifier(config.label, 'catalog.label');
|
|
17
|
+
const selectParts = [`[${config.value}] AS [value]`, `[${config.label}] AS [label]`];
|
|
18
|
+
if (config.extra) {
|
|
19
|
+
for (const field of config.extra) {
|
|
20
|
+
assertValidIdentifier(field, 'catalog.extra');
|
|
21
|
+
selectParts.push(`[${field}]`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const whereClause = config.where ? ` WHERE ${config.where}` : '';
|
|
25
|
+
const orderByClause = config.orderBy
|
|
26
|
+
? `ORDER BY ${bracketOrderBy(config.orderBy)}`
|
|
27
|
+
: `ORDER BY [${config.label}] ASC`;
|
|
28
|
+
return `SELECT ${selectParts.join(', ')} FROM [${config.from}]${whereClause} ${orderByClause}`;
|
|
29
|
+
}
|
|
30
|
+
function bracketOrderBy(orderBy) {
|
|
31
|
+
return orderBy.replace(/^(\w+)(\s+(?:ASC|DESC))?$/i, (_match, col, dir) => {
|
|
32
|
+
return `[${col}]${dir ?? ''}`;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=catalog-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-builder.js","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAE/B,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAE9B,qBAAqB,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAEnD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,qBAAqB,CAAC,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC3D,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO;YAClC,CAAC,CAAC,YAAY,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YAC9C,CAAC,CAAC,aAAa,MAAM,CAAC,QAAQ,OAAO,CAAC;QACxC,OAAO,oBAAoB,MAAM,CAAC,QAAQ,kBAAkB,MAAM,CAAC,QAAQ,sBAAsB,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;IACnI,CAAC;IAED,qBAAqB,CAAC,MAAM,CAAC,KAAM,EAAE,eAAe,CAAC,CAAC;IACtD,qBAAqB,CAAC,MAAM,CAAC,KAAM,EAAE,eAAe,CAAC,CAAC;IAEtD,MAAM,WAAW,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,cAAc,EAAE,IAAI,MAAM,CAAC,KAAK,cAAc,CAAC,CAAC;IAErF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjC,qBAAqB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;YAC9C,WAAW,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO;QAClC,CAAC,CAAC,YAAY,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;QAC9C,CAAC,CAAC,aAAa,MAAM,CAAC,KAAK,OAAO,CAAC;IAErC,OAAO,UAAU,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,IAAI,IAAI,WAAW,IAAI,aAAa,EAAE,CAAC;AACjG,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,OAAO,OAAO,CAAC,OAAO,CAAC,4BAA4B,EAAE,CAAC,MAAM,EAAE,GAAW,EAAE,GAAuB,EAAE,EAAE;QACpG,OAAO,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAY,EAAE,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAgB5F"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import sql from 'mssql';
|
|
2
|
+
import { resolveEnvVars } from './spec-loader.js';
|
|
3
|
+
export async function createConnectionPool(config) {
|
|
4
|
+
const resolved = resolveEnvVars(config);
|
|
5
|
+
const pool = new sql.ConnectionPool({
|
|
6
|
+
server: resolved.server,
|
|
7
|
+
database: resolved.database,
|
|
8
|
+
user: resolved.user,
|
|
9
|
+
password: resolved.password,
|
|
10
|
+
port: resolved.port ?? 1433,
|
|
11
|
+
options: {
|
|
12
|
+
trustServerCertificate: resolved.trustServerCertificate ?? true,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
await pool.connect();
|
|
16
|
+
return pool;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,GAA4B,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAwB;IACjE,MAAM,QAAQ,GAAG,cAAc,CAAC,MAA4C,CAAC,CAAC;IAE9E,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC;QAClC,MAAM,EAAE,QAAQ,CAAC,MAAgB;QACjC,QAAQ,EAAE,QAAQ,CAAC,QAAkB;QACrC,IAAI,EAAE,QAAQ,CAAC,IAA0B;QACzC,QAAQ,EAAE,QAAQ,CAAC,QAA8B;QACjD,IAAI,EAAG,QAAQ,CAAC,IAAe,IAAI,IAAI;QACvC,OAAO,EAAE;YACP,sBAAsB,EAAG,QAAQ,CAAC,sBAAkC,IAAI,IAAI;SAC7E;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACrB,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function filterFields(body: Record<string, unknown>, allowedFields: string[]): Record<string, unknown>;
|
|
2
|
+
export declare function buildInsertQuery(table: string, fields: Record<string, unknown>, primaryKey?: string): {
|
|
3
|
+
sql: string;
|
|
4
|
+
params: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
export declare function buildUpdateQuery(table: string, primaryKey: string, pkValue: unknown, fields: Record<string, unknown>): {
|
|
7
|
+
sql: string;
|
|
8
|
+
params: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
export declare function buildDeleteQuery(table: string, primaryKey: string, pkValue: unknown): {
|
|
11
|
+
sql: string;
|
|
12
|
+
params: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=crud-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud-builder.d.ts","sourceRoot":"","sources":["../src/crud-builder.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS5G;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAelD;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAUlD;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAKlD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { assertValidIdentifier } from './validation/identifier-guard.js';
|
|
2
|
+
export function filterFields(body, allowedFields) {
|
|
3
|
+
const filtered = {};
|
|
4
|
+
const allowed = new Set(allowedFields);
|
|
5
|
+
for (const [key, value] of Object.entries(body)) {
|
|
6
|
+
if (allowed.has(key)) {
|
|
7
|
+
filtered[key] = value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return filtered;
|
|
11
|
+
}
|
|
12
|
+
export function buildInsertQuery(table, fields, primaryKey) {
|
|
13
|
+
assertValidIdentifier(table, 'crud.table');
|
|
14
|
+
const columns = Object.keys(fields);
|
|
15
|
+
for (const col of columns) {
|
|
16
|
+
assertValidIdentifier(col, 'crud field');
|
|
17
|
+
}
|
|
18
|
+
const columnsList = columns.map(c => `[${c}]`).join(', ');
|
|
19
|
+
const paramsList = columns.map(c => `@${c}`).join(', ');
|
|
20
|
+
// Trigger-safe: INSERT + SELECT back by SCOPE_IDENTITY() instead of OUTPUT INSERTED.*
|
|
21
|
+
const selectBack = primaryKey
|
|
22
|
+
? `; SELECT * FROM [${table}] WHERE [${primaryKey}] = SCOPE_IDENTITY()`
|
|
23
|
+
: '';
|
|
24
|
+
const sql = `INSERT INTO [${table}] (${columnsList}) VALUES (${paramsList})${selectBack}`;
|
|
25
|
+
return { sql, params: { ...fields } };
|
|
26
|
+
}
|
|
27
|
+
export function buildUpdateQuery(table, primaryKey, pkValue, fields) {
|
|
28
|
+
assertValidIdentifier(table, 'crud.table');
|
|
29
|
+
assertValidIdentifier(primaryKey, 'crud.primaryKey');
|
|
30
|
+
for (const col of Object.keys(fields)) {
|
|
31
|
+
assertValidIdentifier(col, 'crud field');
|
|
32
|
+
}
|
|
33
|
+
const setClauses = Object.keys(fields).map(c => `[${c}] = @${c}`).join(', ');
|
|
34
|
+
const sql = `UPDATE [${table}] SET ${setClauses} WHERE [${primaryKey}] = @_pkValue; SELECT * FROM [${table}] WHERE [${primaryKey}] = @_pkValue`;
|
|
35
|
+
return { sql, params: { ...fields, _pkValue: pkValue } };
|
|
36
|
+
}
|
|
37
|
+
export function buildDeleteQuery(table, primaryKey, pkValue) {
|
|
38
|
+
assertValidIdentifier(table, 'crud.table');
|
|
39
|
+
assertValidIdentifier(primaryKey, 'crud.primaryKey');
|
|
40
|
+
const sql = `DELETE FROM [${table}] WHERE [${primaryKey}] = @_pkValue`;
|
|
41
|
+
return { sql, params: { _pkValue: pkValue } };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=crud-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud-builder.js","sourceRoot":"","sources":["../src/crud-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Handler } from './types.js';
|
|
2
|
+
export declare function discoverHandlers(handlersDir: string): Promise<Map<string, Handler>>;
|
|
3
|
+
export declare function validateHandlerRefs(refs: string[], handlers: Map<string, Handler>): string[];
|
|
4
|
+
export declare function getHandlerRefs(spec: {
|
|
5
|
+
endpoints?: Record<string, {
|
|
6
|
+
handler?: string;
|
|
7
|
+
}>;
|
|
8
|
+
}): string[];
|
|
9
|
+
//# sourceMappingURL=handler-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-loader.d.ts","sourceRoot":"","sources":["../src/handler-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK1C,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA0BzF;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EAAE,EACd,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,EAAE,CAQV;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,MAAM,EAAE,CAKnG"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
export async function discoverHandlers(handlersDir) {
|
|
5
|
+
const handlers = new Map();
|
|
6
|
+
if (!fs.existsSync(handlersDir)) {
|
|
7
|
+
return handlers;
|
|
8
|
+
}
|
|
9
|
+
const files = fs.readdirSync(handlersDir).filter(f => f.endsWith('.ts') || f.endsWith('.js') || f.endsWith('.mjs'));
|
|
10
|
+
for (const file of files) {
|
|
11
|
+
const name = path.basename(file, path.extname(file));
|
|
12
|
+
const fullPath = path.resolve(handlersDir, file);
|
|
13
|
+
try {
|
|
14
|
+
const mod = await import(pathToFileURL(fullPath).href);
|
|
15
|
+
const handler = mod.default;
|
|
16
|
+
if (typeof handler === 'function') {
|
|
17
|
+
handlers.set(name, handler);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Skip files that fail to import
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return handlers;
|
|
25
|
+
}
|
|
26
|
+
export function validateHandlerRefs(refs, handlers) {
|
|
27
|
+
const errors = [];
|
|
28
|
+
for (const ref of refs) {
|
|
29
|
+
if (!handlers.has(ref)) {
|
|
30
|
+
errors.push(`Handler "${ref}" referenced in spec but not found in handlers directory`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return errors;
|
|
34
|
+
}
|
|
35
|
+
export function getHandlerRefs(spec) {
|
|
36
|
+
if (!spec.endpoints)
|
|
37
|
+
return [];
|
|
38
|
+
return Object.values(spec.endpoints)
|
|
39
|
+
.filter(e => e.handler)
|
|
40
|
+
.map(e => e.handler);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=handler-loader.js.map
|