mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-07-28 13:00:02 +00:00
067cdacde8
Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/3d6885b0-d066-41b0-8042-fa2b629d3709.jpg
2269 lines
67 KiB
TypeScript
2269 lines
67 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'
|
|
*/
|
|
/**
|
|
* @swagger
|
|
* /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:
|
|
* givenName:
|
|
* type: string
|
|
* surname:
|
|
* type: string
|
|
* displayName:
|
|
* type: string
|
|
* email:
|
|
* type: string
|
|
* enabled:
|
|
* type: boolean
|
|
* userPrincipalName:
|
|
* type: string
|
|
* adProperties:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: Updated AD user
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/AdUser'
|
|
* 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);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-groups/{id}:
|
|
* put:
|
|
* summary: Update an AD group
|
|
* tags: [AD Groups]
|
|
* 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
|
|
* name:
|
|
* type: string
|
|
* description:
|
|
* type: string
|
|
* groupScope:
|
|
* type: string
|
|
* enum: [DomainLocal, Global, Universal]
|
|
* groupType:
|
|
* type: string
|
|
* enum: [Security, Distribution]
|
|
* members:
|
|
* type: array
|
|
* items:
|
|
* type: string
|
|
* adProperties:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: Group updated successfully
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/AdGroup'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
* 400:
|
|
* $ref: '#/components/responses/BadRequestError'
|
|
*/
|
|
app.put("/api/connections/:connectionId/ad-groups/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const group = await storage.getAdGroup(parseInt(req.params.id));
|
|
|
|
if (!group || group.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD group not found" });
|
|
}
|
|
|
|
const updatedGroup = await storage.updateAdGroup(parseInt(req.params.id), req.body);
|
|
res.json(updatedGroup);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/connections/{connectionId}/ad-groups/{id}:
|
|
* delete:
|
|
* summary: Delete an AD group
|
|
* tags: [AD Groups]
|
|
* 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: Group deleted successfully
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
app.delete("/api/connections/:connectionId/ad-groups/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const group = await storage.getAdGroup(parseInt(req.params.id));
|
|
|
|
if (!group || group.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD group not found" });
|
|
}
|
|
|
|
const deleted = await storage.deleteAdGroup(parseInt(req.params.id));
|
|
|
|
if (!deleted) {
|
|
return res.status(404).json({ message: "AD group not found" });
|
|
}
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// Similar endpoints for AD Groups
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-groups:
|
|
* get:
|
|
* summary: List AD groups from the specified LDAP connection
|
|
* tags: [AD Groups]
|
|
* 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 groups
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/AdGroup'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
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);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-org-units/{id}:
|
|
* put:
|
|
* summary: Update an AD organizational unit
|
|
* tags: [AD Organizational Units]
|
|
* 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
|
|
* name:
|
|
* type: string
|
|
* description:
|
|
* type: string
|
|
* parentOu:
|
|
* type: string
|
|
* adProperties:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: Organizational unit updated successfully
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/AdOrgUnit'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
* 400:
|
|
* $ref: '#/components/responses/BadRequestError'
|
|
*/
|
|
app.put("/api/connections/:connectionId/ad-org-units/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const orgUnit = await storage.getAdOrgUnit(parseInt(req.params.id));
|
|
|
|
if (!orgUnit || orgUnit.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD organizational unit not found" });
|
|
}
|
|
|
|
const updatedOrgUnit = await storage.updateAdOrgUnit(parseInt(req.params.id), req.body);
|
|
res.json(updatedOrgUnit);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/connections/{connectionId}/ad-org-units/{id}:
|
|
* delete:
|
|
* summary: Delete an AD organizational unit
|
|
* tags: [AD Organizational Units]
|
|
* 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: Organizational unit deleted successfully
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
app.delete("/api/connections/:connectionId/ad-org-units/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const orgUnit = await storage.getAdOrgUnit(parseInt(req.params.id));
|
|
|
|
if (!orgUnit || orgUnit.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD organizational unit not found" });
|
|
}
|
|
|
|
const deleted = await storage.deleteAdOrgUnit(parseInt(req.params.id));
|
|
|
|
if (!deleted) {
|
|
return res.status(404).json({ message: "AD organizational unit not found" });
|
|
}
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// Organizational Units endpoints
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-org-units:
|
|
* get:
|
|
* summary: List AD organizational units from the specified LDAP connection
|
|
* tags: [AD Organizational Units]
|
|
* 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 organizational units
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/AdOrgUnit'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
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);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-computers/{id}:
|
|
* put:
|
|
* summary: Update an AD computer
|
|
* tags: [AD Computers]
|
|
* 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
|
|
* name:
|
|
* type: string
|
|
* dnsHostName:
|
|
* type: string
|
|
* description:
|
|
* type: string
|
|
* operatingSystem:
|
|
* type: string
|
|
* operatingSystemVersion:
|
|
* type: string
|
|
* enabled:
|
|
* type: boolean
|
|
* adProperties:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: Computer updated successfully
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/AdComputer'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
* 400:
|
|
* $ref: '#/components/responses/BadRequestError'
|
|
*/
|
|
app.put("/api/connections/:connectionId/ad-computers/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const computer = await storage.getAdComputer(parseInt(req.params.id));
|
|
|
|
if (!computer || computer.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD computer not found" });
|
|
}
|
|
|
|
const updatedComputer = await storage.updateAdComputer(parseInt(req.params.id), req.body);
|
|
res.json(updatedComputer);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/connections/{connectionId}/ad-computers/{id}:
|
|
* delete:
|
|
* summary: Delete an AD computer
|
|
* tags: [AD Computers]
|
|
* 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: Computer deleted successfully
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
app.delete("/api/connections/:connectionId/ad-computers/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const computer = await storage.getAdComputer(parseInt(req.params.id));
|
|
|
|
if (!computer || computer.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD computer not found" });
|
|
}
|
|
|
|
const deleted = await storage.deleteAdComputer(parseInt(req.params.id));
|
|
|
|
if (!deleted) {
|
|
return res.status(404).json({ message: "AD computer not found" });
|
|
}
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// Computers endpoints
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-computers:
|
|
* get:
|
|
* summary: List AD computers from the specified LDAP connection
|
|
* tags: [AD Computers]
|
|
* 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 computers
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/AdComputer'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
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);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-domains/{id}:
|
|
* put:
|
|
* summary: Update an AD domain
|
|
* tags: [AD Domains]
|
|
* 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
|
|
* name:
|
|
* type: string
|
|
* netBIOSName:
|
|
* type: string
|
|
* forestName:
|
|
* type: string
|
|
* domainFunctionality:
|
|
* type: string
|
|
* adProperties:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: Domain updated successfully
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/AdDomain'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
* 400:
|
|
* $ref: '#/components/responses/BadRequestError'
|
|
*/
|
|
app.put("/api/connections/:connectionId/ad-domains/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const domain = await storage.getAdDomain(parseInt(req.params.id));
|
|
|
|
if (!domain || domain.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD domain not found" });
|
|
}
|
|
|
|
const updatedDomain = await storage.updateAdDomain(parseInt(req.params.id), req.body);
|
|
res.json(updatedDomain);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /api/connections/{connectionId}/ad-domains/{id}:
|
|
* delete:
|
|
* summary: Delete an AD domain
|
|
* tags: [AD Domains]
|
|
* 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: Domain deleted successfully
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
app.delete("/api/connections/:connectionId/ad-domains/:id", authenticateApiToken, async (req, res, next) => {
|
|
try {
|
|
const domain = await storage.getAdDomain(parseInt(req.params.id));
|
|
|
|
if (!domain || domain.connectionId !== parseInt(req.params.connectionId)) {
|
|
return res.status(404).json({ message: "AD domain not found" });
|
|
}
|
|
|
|
const deleted = await storage.deleteAdDomain(parseInt(req.params.id));
|
|
|
|
if (!deleted) {
|
|
return res.status(404).json({ message: "AD domain not found" });
|
|
}
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// Domains endpoints
|
|
/**
|
|
* @swagger
|
|
* /connections/{connectionId}/ad-domains:
|
|
* get:
|
|
* summary: List AD domains from the specified LDAP connection
|
|
* tags: [AD Domains]
|
|
* 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 domains
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/AdDomain'
|
|
* 401:
|
|
* $ref: '#/components/responses/UnauthorizedError'
|
|
* 404:
|
|
* $ref: '#/components/responses/NotFoundError'
|
|
*/
|
|
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;
|
|
}
|