mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-07-26 11:59:14 +00:00
300afeb0db
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/91bfedad-c6cf-43ef-9c07-9b11ffcf4a58.jpg
519 lines
19 KiB
TypeScript
519 lines
19 KiB
TypeScript
import swaggerJsdoc from "swagger-jsdoc";
|
|
import swaggerUi from "swagger-ui-express";
|
|
import { Express, Request, Response } from "express";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import { getQueryParametersDocumentation } from "./query-parser";
|
|
import debugLib from "debug";
|
|
|
|
const debug = debugLib('api:swagger');
|
|
|
|
// Swagger definition
|
|
const swaggerOptions = {
|
|
definition: {
|
|
openapi: "3.0.0",
|
|
info: {
|
|
title: "Active Directory Management API",
|
|
version: "1.0.0",
|
|
description: "REST API for managing Active Directory resources. This API provides comprehensive capabilities for managing Active Directory users, groups, organizational units, and more through a RESTful interface.",
|
|
contact: {
|
|
name: "API Support",
|
|
email: "support@example.com",
|
|
},
|
|
license: {
|
|
name: "MIT",
|
|
url: "https://opensource.org/licenses/MIT",
|
|
}
|
|
},
|
|
externalDocs: {
|
|
description: "Find out more about this API",
|
|
url: "/api/docs/more-info"
|
|
},
|
|
servers: [
|
|
{
|
|
url: "/api",
|
|
description: "API base URL",
|
|
},
|
|
],
|
|
components: {
|
|
securitySchemes: {
|
|
bearerAuth: {
|
|
type: "http",
|
|
scheme: "bearer",
|
|
bearerFormat: "JWT",
|
|
},
|
|
cookieAuth: {
|
|
type: "apiKey",
|
|
in: "cookie",
|
|
name: "connect.sid",
|
|
},
|
|
},
|
|
schemas: {
|
|
LdapConnection: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
name: { type: "string" },
|
|
server: { type: "string" },
|
|
domain: { type: "string" },
|
|
port: { type: "integer" },
|
|
useSSL: { type: "boolean" },
|
|
username: { type: "string" },
|
|
status: { type: "string", enum: ["connected", "disconnected"] },
|
|
lastConnected: { type: "string", format: "date-time" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
AdUser: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
objectGUID: { type: "string" },
|
|
distinguishedName: { type: "string" },
|
|
canonicalName: { type: "string" },
|
|
cn: { type: "string" },
|
|
sAMAccountName: { type: "string" },
|
|
userPrincipalName: { type: "string" },
|
|
givenName: { type: "string" },
|
|
surname: { type: "string" },
|
|
displayName: { type: "string" },
|
|
email: { type: "string" },
|
|
enabled: { type: "boolean" },
|
|
lastLogon: { type: "string", format: "date-time" },
|
|
memberOf: { type: "array", items: { type: "string" } },
|
|
managedBy: { type: "string", description: "Distinguished name of the user or group managing this object" },
|
|
adProperties: { type: "object" },
|
|
objectType: { type: "string", enum: ["user"] },
|
|
},
|
|
},
|
|
AdGroup: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
objectGUID: { type: "string" },
|
|
distinguishedName: { type: "string" },
|
|
canonicalName: { type: "string" },
|
|
cn: { type: "string" },
|
|
sAMAccountName: { type: "string" },
|
|
groupType: { type: "string" },
|
|
description: { type: "string" },
|
|
members: { type: "array", items: { type: "string" } },
|
|
managedBy: { type: "string", description: "Distinguished name of the user or group managing this object" },
|
|
adProperties: { type: "object" },
|
|
objectType: { type: "string", enum: ["group"] },
|
|
},
|
|
},
|
|
AdOrgUnit: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
objectGUID: { type: "string" },
|
|
distinguishedName: { type: "string" },
|
|
canonicalName: { type: "string" },
|
|
cn: { type: "string" },
|
|
name: { type: "string" },
|
|
description: { type: "string" },
|
|
managedBy: { type: "string", description: "Distinguished name of the user or group managing this object" },
|
|
adProperties: { type: "object" },
|
|
objectType: { type: "string", enum: ["organizationalUnit"] },
|
|
},
|
|
},
|
|
AdComputer: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
objectGUID: { type: "string" },
|
|
distinguishedName: { type: "string" },
|
|
canonicalName: { type: "string" },
|
|
cn: { type: "string" },
|
|
name: { type: "string" },
|
|
dnsHostName: { type: "string" },
|
|
operatingSystem: { type: "string" },
|
|
operatingSystemVersion: { type: "string" },
|
|
lastLogon: { type: "string", format: "date-time" },
|
|
enabled: { type: "boolean" },
|
|
managedBy: { type: "string", description: "Distinguished name of the user or group managing this object" },
|
|
adProperties: { type: "object" },
|
|
objectType: { type: "string", enum: ["computer"] },
|
|
},
|
|
},
|
|
AdDomain: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
objectGUID: { type: "string" },
|
|
distinguishedName: { type: "string" },
|
|
canonicalName: { type: "string" },
|
|
cn: { type: "string" },
|
|
name: { type: "string" },
|
|
netBIOSName: { type: "string" },
|
|
forestName: { type: "string" },
|
|
domainFunctionality: { type: "string" },
|
|
adProperties: { type: "object" },
|
|
objectType: { type: "string", enum: ["domain"] },
|
|
},
|
|
},
|
|
AdSite: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
objectGUID: { type: "string" },
|
|
distinguishedName: { type: "string" },
|
|
cn: { type: "string" },
|
|
name: { type: "string" },
|
|
description: { type: "string" },
|
|
location: { type: "string" },
|
|
managedBy: { type: "string", description: "Distinguished name of the user or group managing this site" },
|
|
adProperties: { type: "object" },
|
|
objectType: { type: "string", enum: ["site"] },
|
|
},
|
|
},
|
|
AdSubnet: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
objectGUID: { type: "string" },
|
|
distinguishedName: { type: "string" },
|
|
cn: { type: "string" },
|
|
name: { type: "string" },
|
|
description: { type: "string" },
|
|
siteObject: { type: "string", description: "Distinguished name of the site this subnet belongs to" },
|
|
location: { type: "string" },
|
|
networkAddress: { type: "string" },
|
|
networkMask: { type: "string" },
|
|
cidr: { type: "string", description: "CIDR notation for the subnet (e.g., 192.168.1.0/24)" },
|
|
managedBy: { type: "string", description: "Distinguished name of the user or group managing this subnet" },
|
|
adProperties: { type: "object" },
|
|
objectType: { type: "string", enum: ["subnet"] },
|
|
},
|
|
},
|
|
LdapAttribute: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
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" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
updatedAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
LdapFilter: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
connectionId: { type: "integer" },
|
|
name: { type: "string" },
|
|
description: { type: "string" },
|
|
objectClass: { type: "string", enum: ["user", "group", "organizationalUnit", "computer", "domain"] },
|
|
filter: { type: "object" },
|
|
ldapFilter: { type: "string" },
|
|
createdBy: { type: "integer" },
|
|
modifiedBy: { type: "integer" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
updatedAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
LdapFilterRevision: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
filterId: { type: "integer" },
|
|
version: { type: "integer" },
|
|
filter: { type: "object" },
|
|
ldapFilter: { type: "string" },
|
|
createdBy: { type: "integer" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
comment: { type: "string" },
|
|
},
|
|
},
|
|
Error: {
|
|
type: "object",
|
|
properties: {
|
|
message: { type: "string" },
|
|
errors: {
|
|
type: "array",
|
|
items: {
|
|
type: "object",
|
|
properties: {
|
|
path: { type: "array", items: { type: "string" } },
|
|
message: { type: "string" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
parameters: {
|
|
filterParam: {
|
|
name: "filter",
|
|
in: "query",
|
|
description: "Filter expression (e.g. name eq 'John')",
|
|
schema: { type: "string" },
|
|
},
|
|
selectParam: {
|
|
name: "select",
|
|
in: "query",
|
|
description: "Properties to select (comma-separated)",
|
|
schema: { type: "string" },
|
|
},
|
|
expandParam: {
|
|
name: "expand",
|
|
in: "query",
|
|
description: "Related entities to expand (comma-separated)",
|
|
schema: { type: "string" },
|
|
},
|
|
orderByParam: {
|
|
name: "orderBy",
|
|
in: "query",
|
|
description: "Property to order by (e.g. name asc)",
|
|
schema: { type: "string" },
|
|
},
|
|
topParam: {
|
|
name: "top",
|
|
in: "query",
|
|
description: "Number of records to return",
|
|
schema: { type: "integer" },
|
|
},
|
|
skipParam: {
|
|
name: "skip",
|
|
in: "query",
|
|
description: "Number of records to skip",
|
|
schema: { type: "integer" },
|
|
},
|
|
AuditLog: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "integer" },
|
|
action: { type: "string" },
|
|
targetId: { type: "string" },
|
|
details: {
|
|
type: "object",
|
|
additionalProperties: true
|
|
},
|
|
userId: { type: "integer" },
|
|
timestamp: { type: "string", format: "date-time" },
|
|
connectionId: { type: "integer" }
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
UnauthorizedError: {
|
|
description: "Authentication required",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
BadRequestError: {
|
|
description: "Invalid request",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
NotFoundError: {
|
|
description: "Resource not found",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
PaginatedResponse: {
|
|
description: "Successful response with pagination metadata",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
data: {
|
|
type: "array",
|
|
items: {
|
|
type: "object"
|
|
}
|
|
},
|
|
metadata: {
|
|
type: "object",
|
|
properties: {
|
|
currentPage: { type: "integer" },
|
|
totalPages: { type: "integer" },
|
|
totalRecords: { type: "integer" },
|
|
nextPage: { type: "string", nullable: true },
|
|
previousPage: { type: "string", nullable: true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
security: [
|
|
{
|
|
bearerAuth: [],
|
|
},
|
|
{
|
|
cookieAuth: [],
|
|
},
|
|
],
|
|
},
|
|
apis: ["./server/routes.ts"], // Path to the API routes
|
|
};
|
|
|
|
const swaggerSpec = swaggerJsdoc(swaggerOptions);
|
|
|
|
export function setupSwagger(app: Express) {
|
|
// Configure Swagger UI with additional options
|
|
const swaggerUiOptions = {
|
|
explorer: true,
|
|
swaggerOptions: {
|
|
persistAuthorization: true,
|
|
docExpansion: 'none',
|
|
tagsSorter: 'alpha',
|
|
operationsSorter: 'alpha',
|
|
filter: true,
|
|
},
|
|
};
|
|
|
|
// Mount at both paths for backward compatibility
|
|
app.use("/api/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec, swaggerUiOptions));
|
|
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec, swaggerUiOptions));
|
|
|
|
// Provide the JSON spec at multiple paths
|
|
app.get("/api/swagger.json", (req, res) => {
|
|
res.setHeader("Content-Type", "application/json");
|
|
res.send(swaggerSpec);
|
|
});
|
|
|
|
app.get("/api-docs/swagger.json", (req, res) => {
|
|
res.setHeader("Content-Type", "application/json");
|
|
res.send(swaggerSpec);
|
|
});
|
|
|
|
// Add download endpoint for the OpenAPI specification
|
|
app.get("/api/docs/download", (req, res) => {
|
|
const format = req.query.format?.toString().toLowerCase() || 'json';
|
|
|
|
if (format === 'json') {
|
|
res.setHeader('Content-Disposition', 'attachment; filename=openapi-spec.json');
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.send(JSON.stringify(swaggerSpec, null, 2));
|
|
} else if (format === 'yaml' || format === 'yml') {
|
|
try {
|
|
// We'll need to import yaml dynamically or handle YAML conversion manually
|
|
// For simplicity, just sending JSON if yaml not supported
|
|
res.setHeader('Content-Disposition', 'attachment; filename=openapi-spec.json');
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.send(JSON.stringify(swaggerSpec, null, 2));
|
|
debug('YAML download requested but not implemented, sending JSON');
|
|
} catch (err) {
|
|
debug('Error generating YAML:', err);
|
|
res.setHeader('Content-Disposition', 'attachment; filename=openapi-spec.json');
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.send(JSON.stringify(swaggerSpec, null, 2));
|
|
}
|
|
} else {
|
|
res.status(400).send({ error: 'Invalid format. Supported formats: json, yaml' });
|
|
}
|
|
});
|
|
|
|
// Add documentation for query parameters and filtering
|
|
app.get("/api/docs/more-info", (req, res) => {
|
|
const queryParamsInfo = getQueryParametersDocumentation();
|
|
res.send(`
|
|
<html>
|
|
<head>
|
|
<title>Active Directory Management API - Advanced Usage</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
h1, h2, h3 { color: #0066cc; }
|
|
pre { background-color: #f5f5f5; padding: 10px; border-radius: 5px; overflow-x: auto; }
|
|
code { background-color: #f5f5f5; padding: 2px 4px; border-radius: 3px; }
|
|
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
|
|
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
th { background-color: #f2f2f2; }
|
|
tr:nth-child(even) { background-color: #f9f9f9; }
|
|
.btn { display: inline-block; padding: 10px 15px; background-color: #0066cc; color: white; text-decoration: none; border-radius: 5px; margin: 10px 0; }
|
|
.btn:hover { background-color: #004c99; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Active Directory Management API - Advanced Usage Guide</h1>
|
|
|
|
<h2>API Documentation</h2>
|
|
<p>The complete API documentation is available at <a href="/api/docs">/api/docs</a>.</p>
|
|
<p>You can also download the OpenAPI specification:</p>
|
|
<p>
|
|
<a href="/api/docs/download?format=json" class="btn">Download OpenAPI Spec (JSON)</a>
|
|
<a href="/api/docs/download?format=yaml" class="btn">Download OpenAPI Spec (YAML)</a>
|
|
</p>
|
|
|
|
<h2>Query Parameters and Filtering</h2>
|
|
<pre>${queryParamsInfo}</pre>
|
|
|
|
<h2>Authentication</h2>
|
|
<p>This API supports two authentication methods:</p>
|
|
<ul>
|
|
<li><strong>Session-based authentication</strong>: Used when accessing the API from the web interface.</li>
|
|
<li><strong>API Token authentication</strong>: Used when accessing the API programmatically.</li>
|
|
</ul>
|
|
|
|
<h3>API Token Authentication</h3>
|
|
<p>To authenticate using an API token, include the token in the Authorization header:</p>
|
|
<pre>Authorization: Bearer YOUR_API_TOKEN</pre>
|
|
|
|
<h2>Rate Limiting</h2>
|
|
<p>The API has rate limiting in place to prevent abuse. The current limits are:</p>
|
|
<ul>
|
|
<li>100 requests per minute for authenticated users</li>
|
|
<li>20 requests per minute for unauthenticated users</li>
|
|
</ul>
|
|
|
|
<h2>Error Handling</h2>
|
|
<p>The API returns consistent error responses with the following structure:</p>
|
|
<pre>{
|
|
"message": "Error message",
|
|
"errors": [
|
|
{
|
|
"path": ["field", "subfield"],
|
|
"message": "Specific error message"
|
|
}
|
|
]
|
|
}</pre>
|
|
|
|
<h2>Example Usage</h2>
|
|
<h3>Filter Users by Name</h3>
|
|
<pre>GET /api/connections/1/ad-users?filter=displayName contains 'John'</pre>
|
|
|
|
<h3>Select Specific Fields</h3>
|
|
<pre>GET /api/connections/1/ad-users?select=id,displayName,email</pre>
|
|
|
|
<h3>Pagination</h3>
|
|
<pre>GET /api/connections/1/ad-users?top=10&skip=20</pre>
|
|
|
|
<h3>Combining Parameters</h3>
|
|
<pre>GET /api/connections/1/ad-users?filter=enabled eq true&select=id,displayName,email&orderBy=displayName asc&top=10</pre>
|
|
|
|
<p>Return to <a href="/api/docs">API Documentation</a></p>
|
|
</body>
|
|
</html>
|
|
`);
|
|
});
|
|
}
|