Files
ActiveDirectoryManager/server/routes.ts
T
2025-04-09 00:56:13 +00:00

1604 lines
47 KiB
TypeScript

import type { Express, Request as ExpressRequest, Response, NextFunction } from "express";
import { createServer, type Server } from "http";
import { setupAuth } from "./auth";
import { setupSwagger } from "./swagger";
import { storage } from "./storage";
import { apiQuerySchema, PERMISSIONS } from "@shared/schema";
import { ZodError } from "zod";
import rateLimit from "express-rate-limit";
import {
requireAuth,
requirePermission,
requireAdmin,
initializeRBAC
} from "./authorization";
// Extend Express Request to include user property
interface Request extends ExpressRequest {
user: {
id: number;
username: string;
roleId?: number;
[key: string]: any;
};
}
export async function registerRoutes(app: Express): Promise<Server> {
// Setup authentication
const { authenticateApiToken } = setupAuth(app);
// Setup Swagger documentation
setupSwagger(app);
// Initialize Role Based Access Control system
await initializeRBAC();
// Apply rate limiting middleware for API routes
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
message: { message: 'Too many requests, please try again later.' },
skip: (req: any) => {
// Skip rate limiting for authenticated users with admin role
if (req.isAuthenticated && typeof req.isAuthenticated === 'function' && req.isAuthenticated()) {
return req.user?.role === 'admin';
}
return false;
}
});
// Apply the rate limiter to API routes
app.use('/api/', apiLimiter);
// Error handler for Zod validation errors
const handleZodError = (err: ZodError, res: Response) => {
return res.status(400).json({
message: "Validation error",
errors: err.errors,
});
};
// Parse query parameters
const parseQueryParams = (req: Request) => {
try {
return apiQuerySchema.parse(req.query);
} catch (err) {
if (err instanceof ZodError) {
console.error("Query parameter validation error:", err.errors);
return undefined;
}
throw err;
}
};
/**
* @swagger
* /api/ldap-connections:
* get:
* summary: List all LDAP connections
* tags: [LDAP Connections]
* security:
* - cookieAuth: []
* - bearerAuth: []
* responses:
* 200:
* description: A list of LDAP connections
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/LdapConnection'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 403:
* $ref: '#/components/responses/ForbiddenError'
*/
app.get("/api/ldap-connections", requirePermission(PERMISSIONS.VIEW_LDAP_CONNECTIONS, { allowApiToken: true }), async (req, res, next) => {
try {
const connections = await storage.listLdapConnections();
// Hide sensitive fields like password
const safeConnections = connections.map(conn => {
const { password, ...safeConn } = conn;
return safeConn;
});
res.json(safeConnections);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-connections:
* post:
* summary: Create a new LDAP connection
* tags: [LDAP Connections]
* security:
* - cookieAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* - server
* - domain
* - username
* - password
* properties:
* name:
* type: string
* server:
* type: string
* domain:
* type: string
* port:
* type: integer
* default: 389
* useSSL:
* type: boolean
* default: true
* username:
* type: string
* password:
* type: string
* responses:
* 201:
* description: Connection created successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapConnection'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
*/
app.post("/api/ldap-connections", requireAdmin, async (req, res, next) => {
try {
const connection = await storage.createLdapConnection(req.body);
// Hide password in response
const { password, ...safeConn } = connection;
res.status(201).json(safeConn);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-connections/{id}:
* get:
* summary: Get a specific LDAP connection
* tags: [LDAP Connections]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: LDAP connection details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapConnection'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.get("/api/ldap-connections/:id", async (req, res, next) => {
try {
if (!req.isAuthenticated()) {
return res.status(401).json({ message: "Not authenticated" });
}
const connection = await storage.getLdapConnection(parseInt(req.params.id));
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
// Hide password in response
const { password, ...safeConn } = connection;
res.json(safeConn);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-connections/{id}:
* put:
* summary: Update a LDAP connection
* tags: [LDAP Connections]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* server:
* type: string
* domain:
* type: string
* port:
* type: integer
* useSSL:
* type: boolean
* username:
* type: string
* password:
* type: string
* responses:
* 200:
* description: Connection updated successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapConnection'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.put("/api/ldap-connections/:id", requireAdmin, async (req, res, next) => {
try {
const updatedConnection = await storage.updateLdapConnection(parseInt(req.params.id), req.body);
if (!updatedConnection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
// Hide password in response
const { password, ...safeConn } = updatedConnection;
res.json(safeConn);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-connections/{id}:
* delete:
* summary: Delete a LDAP connection
* tags: [LDAP Connections]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: Connection deleted successfully
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.delete("/api/ldap-connections/:id", requireAdmin, async (req, res, next) => {
try {
const deleted = await storage.deleteLdapConnection(parseInt(req.params.id));
if (!deleted) {
return res.status(404).json({ message: "LDAP connection not found" });
}
res.json({ success: true });
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/tokens:
* get:
* summary: List all API tokens for current user
* tags: [API Tokens]
* security:
* - cookieAuth: []
* responses:
* 200:
* description: A list of API tokens
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/ApiToken'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
*/
app.get("/api/tokens", async (req, res, next) => {
try {
if (!req.isAuthenticated()) {
return res.status(401).json({ message: "Not authenticated" });
}
const tokens = await storage.listApiTokensByUserId(req.user.id);
res.json(tokens);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/tokens/{id}:
* delete:
* summary: Delete an API token
* tags: [API Tokens]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: Token deleted successfully
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.delete("/api/tokens/:id", async (req, res, next) => {
try {
if (!req.isAuthenticated()) {
return res.status(401).json({ message: "Not authenticated" });
}
const token = await storage.getApiToken(parseInt(req.params.id));
if (!token) {
return res.status(404).json({ message: "Token not found" });
}
// Only allow users to delete their own tokens unless they're admin
if (token.userId !== req.user.id) {
// Get the user's role
const userRole = await storage.getRole(req.user.roleId!);
if (userRole?.name !== "admin") {
return res.status(403).json({ message: "Forbidden: You cannot delete tokens that don't belong to you" });
}
}
const deleted = await storage.deleteApiToken(parseInt(req.params.id));
if (!deleted) {
return res.status(404).json({ message: "Token not found" });
}
res.json({ success: true });
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/users:
* get:
* summary: List all users (admin only)
* tags: [Users]
* security:
* - cookieAuth: []
* responses:
* 200:
* description: A list of users
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/User'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 403:
* description: Forbidden - admin access required
*/
app.get("/api/users", requireAdmin, async (req, res, next) => {
try {
const users = await storage.listUsers();
// Remove passwords from response
const safeUsers = users.map(user => {
const { password, ...safeUser } = user;
return safeUser;
});
res.json(safeUsers);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ad-users:
* get:
* summary: List AD users from the specified LDAP connection
* tags: [AD Users]
* security:
* - bearerAuth: []
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* - $ref: '#/components/parameters/filterParam'
* - $ref: '#/components/parameters/selectParam'
* - $ref: '#/components/parameters/expandParam'
* - $ref: '#/components/parameters/orderByParam'
* - $ref: '#/components/parameters/topParam'
* - $ref: '#/components/parameters/skipParam'
* responses:
* 200:
* description: A list of AD users
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/AdUser'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
*/
app.get("/api/connections/:connectionId/ad-users", async (req, res, next) => {
try {
if (!req.isAuthenticated() && !req.headers.authorization) {
return res.status(401).json({ message: "Authentication required" });
}
const connectionId = parseInt(req.params.connectionId);
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const query = parseQueryParams(req);
const users = await storage.listAdUsers(connectionId, query);
res.json(users);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ad-users:
* post:
* summary: Create a new AD user
* tags: [AD Users]
* security:
* - bearerAuth: []
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - distinguishedName
* - sAMAccountName
* properties:
* distinguishedName:
* type: string
* sAMAccountName:
* type: string
* userPrincipalName:
* type: string
* givenName:
* type: string
* surname:
* type: string
* displayName:
* type: string
* email:
* type: string
* enabled:
* type: boolean
* adProperties:
* type: object
* responses:
* 201:
* description: User created successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AdUser'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
*/
app.post("/api/connections/:connectionId/ad-users", authenticateApiToken, async (req, res, next) => {
try {
const connectionId = parseInt(req.params.connectionId);
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const userData = { ...req.body, connectionId };
const user = await storage.createAdUser(userData);
res.status(201).json(user);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ad-users/{id}:
* get:
* summary: Get a specific AD user
* tags: [AD Users]
* security:
* - bearerAuth: []
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* - name: id
* in: path
* required: true
* schema:
* type: integer
* - $ref: '#/components/parameters/selectParam'
* responses:
* 200:
* description: AD user details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AdUser'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.get("/api/connections/:connectionId/ad-users/:id", async (req, res, next) => {
try {
if (!req.isAuthenticated() && !req.headers.authorization) {
return res.status(401).json({ message: "Authentication required" });
}
const user = await storage.getAdUser(parseInt(req.params.id));
if (!user || user.connectionId !== parseInt(req.params.connectionId)) {
return res.status(404).json({ message: "AD user not found" });
}
// Apply property selection if specified
let result = user;
if (req.query.select) {
const properties = (req.query.select as string).split(',');
const selectedUser: any = { id: user.id };
properties.forEach(prop => {
if ((user as any)[prop] !== undefined) {
selectedUser[prop] = (user as any)[prop];
}
});
result = selectedUser as typeof user;
}
res.json(result);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ad-users/{id}:
* put:
* summary: Update an AD user
* tags: [AD Users]
* security:
* - bearerAuth: []
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* - name: id
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* distinguishedName:
* type: string
* sAMAccountName:
* type: string
* userPrincipalName:
* type: string
* givenName:
* type: string
* surname:
* type: string
* displayName:
* type: string
* email:
* type: string
* enabled:
* type: boolean
* adProperties:
* type: object
* responses:
* 200:
* description: User updated successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AdUser'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.put("/api/connections/:connectionId/ad-users/:id", authenticateApiToken, async (req, res, next) => {
try {
const user = await storage.getAdUser(parseInt(req.params.id));
if (!user || user.connectionId !== parseInt(req.params.connectionId)) {
return res.status(404).json({ message: "AD user not found" });
}
const updatedUser = await storage.updateAdUser(parseInt(req.params.id), req.body);
res.json(updatedUser);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ad-users/{id}:
* delete:
* summary: Delete an AD user
* tags: [AD Users]
* security:
* - bearerAuth: []
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* - name: id
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: User deleted successfully
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.delete("/api/connections/:connectionId/ad-users/:id", authenticateApiToken, async (req, res, next) => {
try {
const user = await storage.getAdUser(parseInt(req.params.id));
if (!user || user.connectionId !== parseInt(req.params.connectionId)) {
return res.status(404).json({ message: "AD user not found" });
}
const deleted = await storage.deleteAdUser(parseInt(req.params.id));
if (!deleted) {
return res.status(404).json({ message: "AD user not found" });
}
res.json({ success: true });
} catch (error) {
next(error);
}
});
// Similar endpoints for AD Groups
app.get("/api/connections/:connectionId/ad-groups", async (req, res, next) => {
try {
if (!req.isAuthenticated() && !req.headers.authorization) {
return res.status(401).json({ message: "Authentication required" });
}
const connectionId = parseInt(req.params.connectionId);
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const query = parseQueryParams(req);
const groups = await storage.listAdGroups(connectionId, query);
res.json(groups);
} catch (error) {
next(error);
}
});
// Organizational Units endpoints
app.get("/api/connections/:connectionId/ad-org-units", async (req, res, next) => {
try {
if (!req.isAuthenticated() && !req.headers.authorization) {
return res.status(401).json({ message: "Authentication required" });
}
const connectionId = parseInt(req.params.connectionId);
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const query = parseQueryParams(req);
const orgUnits = await storage.listAdOrgUnits(connectionId, query);
res.json(orgUnits);
} catch (error) {
next(error);
}
});
// Computers endpoints
app.get("/api/connections/:connectionId/ad-computers", async (req, res, next) => {
try {
if (!req.isAuthenticated() && !req.headers.authorization) {
return res.status(401).json({ message: "Authentication required" });
}
const connectionId = parseInt(req.params.connectionId);
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const query = parseQueryParams(req);
const computers = await storage.listAdComputers(connectionId, query);
res.json(computers);
} catch (error) {
next(error);
}
});
// Domains endpoints
app.get("/api/connections/:connectionId/ad-domains", async (req, res, next) => {
try {
if (!req.isAuthenticated() && !req.headers.authorization) {
return res.status(401).json({ message: "Authentication required" });
}
const connectionId = parseInt(req.params.connectionId);
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const query = parseQueryParams(req);
const domains = await storage.listAdDomains(connectionId, query);
res.json(domains);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ldap-attributes:
* get:
* summary: Get LDAP attributes for a specific object class
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* - name: objectClass
* in: query
* required: true
* schema:
* type: string
* enum: [user, group, organizationalUnit, computer, domain]
* responses:
* 200:
* description: List of LDAP attributes
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/LdapAttribute'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.get("/api/connections/:connectionId/ldap-attributes", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const connectionId = parseInt(req.params.connectionId);
const objectClass = req.query.objectClass as string;
if (!objectClass) {
return res.status(400).json({ message: "objectClass parameter is required" });
}
// Verify the connection exists
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const attributes = await storage.getLdapAttributes(connectionId, objectClass);
res.json(attributes);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ldap-attributes:
* post:
* summary: Create a new LDAP attribute
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* - objectClass
* properties:
* name:
* type: string
* displayName:
* type: string
* description:
* type: string
* type:
* type: string
* multiValued:
* type: boolean
* objectClass:
* type: string
* enum: [user, group, organizationalUnit, computer, domain]
* isIndexed:
* type: boolean
* responses:
* 201:
* description: Attribute created successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapAttribute'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
*/
app.post("/api/connections/:connectionId/ldap-attributes", requireAdmin, async (req, res, next) => {
try {
const connectionId = parseInt(req.params.connectionId);
// Verify the connection exists
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const attribute = await storage.createLdapAttribute({
...req.body,
connectionId
});
res.status(201).json(attribute);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-attributes/{id}:
* put:
* summary: Update an LDAP attribute
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* displayName:
* type: string
* description:
* type: string
* type:
* type: string
* multiValued:
* type: boolean
* isIndexed:
* type: boolean
* responses:
* 200:
* description: Attribute updated successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapAttribute'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.put("/api/ldap-attributes/:id", requireAdmin, async (req, res, next) => {
try {
const id = parseInt(req.params.id);
const updatedAttribute = await storage.updateLdapAttribute(id, req.body);
if (!updatedAttribute) {
return res.status(404).json({ message: "LDAP attribute not found" });
}
res.json(updatedAttribute);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-attributes/{id}:
* delete:
* summary: Delete an LDAP attribute
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: Attribute deleted successfully
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.delete("/api/ldap-attributes/:id", requireAdmin, async (req, res, next) => {
try {
const id = parseInt(req.params.id);
const deleted = await storage.deleteLdapAttribute(id);
if (!deleted) {
return res.status(404).json({ message: "LDAP attribute not found" });
}
res.json({ success: true });
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ldap-filters:
* get:
* summary: List LDAP filters for a connection
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: List of LDAP filters
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/LdapFilter'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
/**
* @swagger
* /api/connections/{connectionId}/test-filter:
* post:
* summary: Test an LDAP filter against a connection
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - ldapFilter
* - objectClass
* properties:
* ldapFilter:
* type: string
* objectClass:
* type: string
* enum: [user, group, organizationalUnit, computer, domain]
* responses:
* 200:
* description: Filter test results
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.post("/api/connections/:connectionId/test-filter", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const connectionId = parseInt(req.params.connectionId);
const { ldapFilter, objectClass } = req.body;
if (!ldapFilter || !objectClass) {
return res.status(400).json({ message: "ldapFilter and objectClass are required" });
}
// Verify the connection exists
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
// Test the filter
const results = await storage.testLdapFilter(connectionId, ldapFilter, objectClass);
res.json(results);
} catch (error) {
next(error);
}
});
app.get("/api/connections/:connectionId/ldap-filters", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const connectionId = parseInt(req.params.connectionId);
// Verify the connection exists
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const filters = await storage.listLdapFilters(connectionId);
res.json(filters);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/ldap-filters:
* post:
* summary: Create a new LDAP filter
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* - objectClass
* - filter
* - ldapFilter
* properties:
* name:
* type: string
* description:
* type: string
* objectClass:
* type: string
* enum: [user, group, organizationalUnit, computer, domain]
* filter:
* type: object
* ldapFilter:
* type: string
* responses:
* 201:
* description: Filter created successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapFilter'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
*/
app.post("/api/connections/:connectionId/ldap-filters", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const connectionId = parseInt(req.params.connectionId);
// Verify the connection exists
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const filter = await storage.createLdapFilter({
...req.body,
connectionId,
createdBy: req.user.id,
modifiedBy: req.user.id
});
res.status(201).json(filter);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-filters/{id}:
* get:
* summary: Get a specific LDAP filter
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: LDAP filter details
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapFilter'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.get("/api/ldap-filters/:id", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const id = parseInt(req.params.id);
const filter = await storage.getLdapFilter(id);
if (!filter) {
return res.status(404).json({ message: "LDAP filter not found" });
}
res.json(filter);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-filters/{id}:
* put:
* summary: Update an LDAP filter
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* description:
* type: string
* filter:
* type: object
* ldapFilter:
* type: string
* isActive:
* type: boolean
* responses:
* 200:
* description: Filter updated successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapFilter'
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.put("/api/ldap-filters/:id", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const id = parseInt(req.params.id);
// Check if the filter exists
const existingFilter = await storage.getLdapFilter(id);
if (!existingFilter) {
return res.status(404).json({ message: "LDAP filter not found" });
}
// Update with the current user as modifier
const updatedFilter = await storage.updateLdapFilter(id, {
...req.body,
modifiedBy: req.user.id
});
res.json(updatedFilter);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-filters/{id}:
* delete:
* summary: Delete an LDAP filter
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: id
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: Filter deleted successfully
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.delete("/api/ldap-filters/:id", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const id = parseInt(req.params.id);
// Check if the filter exists and if the user is allowed to delete it
const filter = await storage.getLdapFilter(id);
if (!filter) {
return res.status(404).json({ message: "LDAP filter not found" });
}
// Only allow the creator or admins to delete
if (filter.createdBy !== req.user.id) {
const userRole = await storage.getRole(req.user.roleId!);
if (userRole?.name !== "admin") {
return res.status(403).json({ message: "You are not authorized to delete this filter" });
}
}
const deleted = await storage.deleteLdapFilter(id);
res.json({ success: deleted });
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-filters/{filterId}/revisions:
* get:
* summary: Get the revision history for an LDAP filter
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: filterId
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: List of filter revisions
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/LdapFilterRevision'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.get("/api/ldap-filters/:filterId/revisions", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const filterId = parseInt(req.params.filterId);
// Check if the filter exists
const filter = await storage.getLdapFilter(filterId);
if (!filter) {
return res.status(404).json({ message: "LDAP filter not found" });
}
const revisions = await storage.getLdapFilterRevisions(filterId);
res.json(revisions);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/ldap-filters/{filterId}/revert/{revisionId}:
* post:
* summary: Revert a filter to a previous revision
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: filterId
* in: path
* required: true
* schema:
* type: integer
* - name: revisionId
* in: path
* required: true
* schema:
* type: integer
* responses:
* 200:
* description: Filter reverted successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/LdapFilter'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
* 404:
* $ref: '#/components/responses/NotFoundError'
*/
app.post("/api/ldap-filters/:filterId/revert/:revisionId", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const filterId = parseInt(req.params.filterId);
const revisionId = parseInt(req.params.revisionId);
// Check if the filter exists
const filter = await storage.getLdapFilter(filterId);
if (!filter) {
return res.status(404).json({ message: "LDAP filter not found" });
}
// Check if the user has permission to modify this filter
if (filter.createdBy !== req.user.id) {
const userRole = await storage.getRole(req.user.roleId!);
if (userRole?.name !== "admin") {
return res.status(403).json({ message: "You are not authorized to modify this filter" });
}
}
// First update the modifiedBy to the current user
await storage.updateLdapFilter(filterId, { modifiedBy: req.user.id });
// Then perform the revert
const revertedFilter = await storage.revertLdapFilterToRevision(filterId, revisionId);
if (!revertedFilter) {
return res.status(404).json({ message: "Failed to revert filter or revision not found" });
}
res.json(revertedFilter);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /api/connections/{connectionId}/test-ldap-filter:
* post:
* summary: Test an LDAP filter against a connection
* tags: [LDAP Query Builder]
* security:
* - cookieAuth: []
* parameters:
* - name: connectionId
* in: path
* required: true
* schema:
* type: integer
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - ldapFilter
* - objectClass
* properties:
* ldapFilter:
* type: string
* objectClass:
* type: string
* enum: [user, group, organizationalUnit, computer, domain]
* responses:
* 200:
* description: Test results
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* 400:
* $ref: '#/components/responses/BadRequestError'
* 401:
* $ref: '#/components/responses/UnauthorizedError'
*/
app.post("/api/connections/:connectionId/test-ldap-filter", requireAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const connectionId = parseInt(req.params.connectionId);
const { ldapFilter, objectClass } = req.body;
if (!ldapFilter || !objectClass) {
return res.status(400).json({ message: "ldapFilter and objectClass are required" });
}
// Verify the connection exists
const connection = await storage.getLdapConnection(connectionId);
if (!connection) {
return res.status(404).json({ message: "LDAP connection not found" });
}
const results = await storage.testLdapFilter(connectionId, ldapFilter, objectClass);
res.json(results);
} catch (error) {
next(error);
}
});
const httpServer = createServer(app);
return httpServer;
}