mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-07-26 20:09:10 +00:00
e871381332
Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90
937 lines
29 KiB
TypeScript
937 lines
29 KiB
TypeScript
import { Router } from 'express';
|
|
import { storage } from './storage';
|
|
import { ldapClient } from './ldap';
|
|
import { randomBytes } from 'crypto';
|
|
import { z } from 'zod';
|
|
import { insertUserSchema, insertApiTokenSchema, insertLdapConnectionSchema } from '@shared/schema';
|
|
|
|
export function registerAPIRoutes(router: Router, verifyApiToken: any) {
|
|
/**
|
|
* @swagger
|
|
* /users:
|
|
* get:
|
|
* summary: Get all users
|
|
* description: Retrieve a list of all users
|
|
* tags: [Users]
|
|
* parameters:
|
|
* - in: query
|
|
* name: properties
|
|
* schema:
|
|
* type: string
|
|
* description: Comma-separated list of properties to return
|
|
* responses:
|
|
* 200:
|
|
* description: A list of users
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/User'
|
|
*/
|
|
router.get('/users', verifyApiToken, async (req, res) => {
|
|
try {
|
|
// Extract properties filter
|
|
const propertiesParam = req.query.properties as string;
|
|
const properties = propertiesParam ? propertiesParam.split(',') : null;
|
|
|
|
const users = await storage.listUsers();
|
|
|
|
// Filter out password field
|
|
const safeUsers = users.map(user => {
|
|
const { password, ...userWithoutPassword } = user;
|
|
return userWithoutPassword;
|
|
});
|
|
|
|
// Apply property selection if specified
|
|
if (properties) {
|
|
const filteredUsers = safeUsers.map(user => {
|
|
const filteredUser: Record<string, any> = {};
|
|
properties.forEach(prop => {
|
|
if (user.hasOwnProperty(prop)) {
|
|
filteredUser[prop] = user[prop as keyof typeof user];
|
|
}
|
|
});
|
|
return filteredUser;
|
|
});
|
|
|
|
return res.json(filteredUsers);
|
|
}
|
|
|
|
res.json(safeUsers);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /users/{id}:
|
|
* get:
|
|
* summary: Get a user by ID
|
|
* description: Retrieve a single user by ID
|
|
* tags: [Users]
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* description: User ID
|
|
* - in: query
|
|
* name: properties
|
|
* schema:
|
|
* type: string
|
|
* description: Comma-separated list of properties to return
|
|
* responses:
|
|
* 200:
|
|
* description: User found
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/User'
|
|
* 404:
|
|
* description: User not found
|
|
*/
|
|
router.get('/users/:id', verifyApiToken, async (req, res) => {
|
|
try {
|
|
const userId = parseInt(req.params.id);
|
|
if (isNaN(userId)) {
|
|
return res.status(400).json({ message: 'Invalid user ID' });
|
|
}
|
|
|
|
// Extract properties filter
|
|
const propertiesParam = req.query.properties as string;
|
|
const properties = propertiesParam ? propertiesParam.split(',') : null;
|
|
|
|
const user = await storage.getUser(userId);
|
|
if (!user) {
|
|
return res.status(404).json({ message: 'User not found' });
|
|
}
|
|
|
|
// Filter out password
|
|
const { password, ...userWithoutPassword } = user;
|
|
|
|
// Apply property selection if specified
|
|
if (properties) {
|
|
const filteredUser: Record<string, any> = {};
|
|
properties.forEach(prop => {
|
|
if (userWithoutPassword.hasOwnProperty(prop)) {
|
|
filteredUser[prop] = userWithoutPassword[prop as keyof typeof userWithoutPassword];
|
|
}
|
|
});
|
|
|
|
return res.json(filteredUser);
|
|
}
|
|
|
|
res.json(userWithoutPassword);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
// API Tokens Routes
|
|
/**
|
|
* @swagger
|
|
* /api-tokens:
|
|
* get:
|
|
* summary: Get all API tokens
|
|
* description: Retrieve a list of all API tokens for the authenticated user
|
|
* tags: [API Tokens]
|
|
* responses:
|
|
* 200:
|
|
* description: A list of API tokens
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/ApiToken'
|
|
*/
|
|
router.get('/api-tokens', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const userId = (req.user as any).id;
|
|
const tokens = await storage.listApiTokens(userId);
|
|
|
|
res.json(tokens);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /api-tokens:
|
|
* post:
|
|
* summary: Create a new API token
|
|
* description: Generate a new API token for the authenticated user
|
|
* tags: [API Tokens]
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* name:
|
|
* type: string
|
|
* expiresAt:
|
|
* type: string
|
|
* format: date-time
|
|
* responses:
|
|
* 201:
|
|
* description: Token created successfully
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/ApiToken'
|
|
*/
|
|
router.post('/api-tokens', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const schema = insertApiTokenSchema.extend({
|
|
name: z.string().min(1, 'Name is required'),
|
|
expiresAt: z.string().optional().transform(val => val ? new Date(val) : undefined)
|
|
});
|
|
|
|
const validateResult = schema.safeParse(req.body);
|
|
|
|
if (!validateResult.success) {
|
|
return res.status(400).json({ message: 'Invalid data', errors: validateResult.error.errors });
|
|
}
|
|
|
|
const { name, expiresAt } = validateResult.data;
|
|
const userId = (req.user as any).id;
|
|
|
|
// Generate a secure token
|
|
const token = randomBytes(32).toString('hex');
|
|
|
|
const apiToken = await storage.createApiToken({
|
|
name,
|
|
token,
|
|
userId,
|
|
expiresAt
|
|
});
|
|
|
|
// Log this activity
|
|
await storage.createActivityLog({
|
|
action: 'Create',
|
|
resource: name,
|
|
resourceType: 'API Token',
|
|
userId,
|
|
username: (req.user as any).username,
|
|
status: 'Success',
|
|
details: { id: apiToken.id }
|
|
});
|
|
|
|
res.status(201).json(apiToken);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /api-tokens/{id}:
|
|
* delete:
|
|
* summary: Delete an API token
|
|
* description: Revoke and delete an API token
|
|
* tags: [API Tokens]
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* description: Token ID
|
|
* responses:
|
|
* 204:
|
|
* description: Token deleted successfully
|
|
* 404:
|
|
* description: Token not found
|
|
*/
|
|
router.delete('/api-tokens/:id', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const tokenId = parseInt(req.params.id);
|
|
if (isNaN(tokenId)) {
|
|
return res.status(400).json({ message: 'Invalid token ID' });
|
|
}
|
|
|
|
const token = await storage.getApiToken(tokenId);
|
|
if (!token) {
|
|
return res.status(404).json({ message: 'Token not found' });
|
|
}
|
|
|
|
// Only allow users to delete their own tokens
|
|
if (token.userId !== (req.user as any).id) {
|
|
return res.status(403).json({ message: 'Not authorized to delete this token' });
|
|
}
|
|
|
|
await storage.deleteApiToken(tokenId);
|
|
|
|
// Log this activity
|
|
await storage.createActivityLog({
|
|
action: 'Delete',
|
|
resource: token.name,
|
|
resourceType: 'API Token',
|
|
userId: (req.user as any).id,
|
|
username: (req.user as any).username,
|
|
status: 'Success',
|
|
details: { id: tokenId }
|
|
});
|
|
|
|
res.status(204).send();
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
// LDAP Connections Routes
|
|
/**
|
|
* @swagger
|
|
* /ldap-connections:
|
|
* get:
|
|
* summary: Get all LDAP connections
|
|
* description: Retrieve a list of all LDAP connections
|
|
* tags: [LDAP Connections]
|
|
* responses:
|
|
* 200:
|
|
* description: A list of LDAP connections
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/LdapConnection'
|
|
*/
|
|
router.get('/ldap-connections', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const connections = await storage.listLdapConnections();
|
|
|
|
// Don't send passwords back
|
|
const safeConnections = connections.map(connection => {
|
|
const { password, ...connectionWithoutPassword } = connection;
|
|
return connectionWithoutPassword;
|
|
});
|
|
|
|
res.json(safeConnections);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ldap-connections:
|
|
* post:
|
|
* summary: Create a new LDAP connection
|
|
* description: Add a new LDAP connection
|
|
* tags: [LDAP Connections]
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* name:
|
|
* type: string
|
|
* server:
|
|
* type: string
|
|
* port:
|
|
* type: integer
|
|
* authType:
|
|
* type: string
|
|
* username:
|
|
* type: string
|
|
* password:
|
|
* type: string
|
|
* baseDN:
|
|
* type: string
|
|
* useTLS:
|
|
* type: boolean
|
|
* responses:
|
|
* 201:
|
|
* description: Connection created successfully
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/LdapConnection'
|
|
*/
|
|
router.post('/ldap-connections', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const schema = insertLdapConnectionSchema.extend({
|
|
name: z.string().min(1, 'Name is required'),
|
|
server: z.string().min(1, 'Server is required'),
|
|
port: z.number().int().positive('Port must be a positive integer'),
|
|
authType: z.string().min(1, 'Authentication type is required'),
|
|
username: z.string().min(1, 'Username is required'),
|
|
password: z.string().min(1, 'Password is required'),
|
|
});
|
|
|
|
const validateResult = schema.safeParse(req.body);
|
|
|
|
if (!validateResult.success) {
|
|
return res.status(400).json({ message: 'Invalid data', errors: validateResult.error.errors });
|
|
}
|
|
|
|
const connection = await storage.createLdapConnection(validateResult.data);
|
|
|
|
// Log this activity
|
|
await storage.createActivityLog({
|
|
action: 'Create',
|
|
resource: connection.name,
|
|
resourceType: 'LDAP Connection',
|
|
userId: (req.user as any).id,
|
|
username: (req.user as any).username,
|
|
status: 'Success',
|
|
details: { id: connection.id }
|
|
});
|
|
|
|
// Don't send password back
|
|
const { password, ...connectionWithoutPassword } = connection;
|
|
|
|
res.status(201).json(connectionWithoutPassword);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ldap-connections/{id}:
|
|
* put:
|
|
* summary: Update an LDAP connection
|
|
* description: Update an existing LDAP connection
|
|
* tags: [LDAP Connections]
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* description: Connection ID
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/LdapConnection'
|
|
* responses:
|
|
* 200:
|
|
* description: Connection updated successfully
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* $ref: '#/components/schemas/LdapConnection'
|
|
* 404:
|
|
* description: Connection not found
|
|
*/
|
|
router.put('/ldap-connections/:id', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const connectionId = parseInt(req.params.id);
|
|
if (isNaN(connectionId)) {
|
|
return res.status(400).json({ message: 'Invalid connection ID' });
|
|
}
|
|
|
|
const connection = await storage.getLdapConnection(connectionId);
|
|
if (!connection) {
|
|
return res.status(404).json({ message: 'Connection not found' });
|
|
}
|
|
|
|
const schema = insertLdapConnectionSchema.partial();
|
|
const validateResult = schema.safeParse(req.body);
|
|
|
|
if (!validateResult.success) {
|
|
return res.status(400).json({ message: 'Invalid data', errors: validateResult.error.errors });
|
|
}
|
|
|
|
const updatedConnection = await storage.updateLdapConnection(connectionId, validateResult.data);
|
|
|
|
// Log this activity
|
|
await storage.createActivityLog({
|
|
action: 'Update',
|
|
resource: connection.name,
|
|
resourceType: 'LDAP Connection',
|
|
userId: (req.user as any).id,
|
|
username: (req.user as any).username,
|
|
status: 'Success',
|
|
details: { id: connectionId }
|
|
});
|
|
|
|
// Don't send password back
|
|
const { password, ...connectionWithoutPassword } = updatedConnection!;
|
|
|
|
res.json(connectionWithoutPassword);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ldap-connections/{id}:
|
|
* delete:
|
|
* summary: Delete an LDAP connection
|
|
* description: Delete an existing LDAP connection
|
|
* tags: [LDAP Connections]
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* description: Connection ID
|
|
* responses:
|
|
* 204:
|
|
* description: Connection deleted successfully
|
|
* 404:
|
|
* description: Connection not found
|
|
*/
|
|
router.delete('/ldap-connections/:id', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const connectionId = parseInt(req.params.id);
|
|
if (isNaN(connectionId)) {
|
|
return res.status(400).json({ message: 'Invalid connection ID' });
|
|
}
|
|
|
|
const connection = await storage.getLdapConnection(connectionId);
|
|
if (!connection) {
|
|
return res.status(404).json({ message: 'Connection not found' });
|
|
}
|
|
|
|
// Disconnect from LDAP if connected
|
|
try {
|
|
await ldapClient.disconnect(connectionId);
|
|
} catch (err) {
|
|
// Ignore disconnect errors
|
|
}
|
|
|
|
await storage.deleteLdapConnection(connectionId);
|
|
|
|
// Log this activity
|
|
await storage.createActivityLog({
|
|
action: 'Delete',
|
|
resource: connection.name,
|
|
resourceType: 'LDAP Connection',
|
|
userId: (req.user as any).id,
|
|
username: (req.user as any).username,
|
|
status: 'Success',
|
|
details: { id: connectionId }
|
|
});
|
|
|
|
res.status(204).send();
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ldap-connections/{id}/test:
|
|
* post:
|
|
* summary: Test an LDAP connection
|
|
* description: Test the connection to an LDAP server
|
|
* tags: [LDAP Connections]
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: integer
|
|
* description: Connection ID
|
|
* responses:
|
|
* 200:
|
|
* description: Connection test successful
|
|
* 400:
|
|
* description: Connection test failed
|
|
* 404:
|
|
* description: Connection not found
|
|
*/
|
|
router.post('/ldap-connections/:id/test', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const connectionId = parseInt(req.params.id);
|
|
if (isNaN(connectionId)) {
|
|
return res.status(400).json({ message: 'Invalid connection ID' });
|
|
}
|
|
|
|
const connection = await storage.getLdapConnection(connectionId);
|
|
if (!connection) {
|
|
return res.status(404).json({ message: 'Connection not found' });
|
|
}
|
|
|
|
try {
|
|
// Disconnect first if already connected
|
|
await ldapClient.disconnect(connectionId);
|
|
|
|
// Try to connect
|
|
const success = await ldapClient.connect(connection);
|
|
|
|
if (success) {
|
|
res.json({ message: 'Connection successful', status: 'connected' });
|
|
} else {
|
|
res.status(400).json({ message: 'Connection failed', status: 'disconnected' });
|
|
}
|
|
} catch (error) {
|
|
res.status(400).json({
|
|
message: 'Connection failed',
|
|
error: error instanceof Error ? error.message : String(error),
|
|
status: 'disconnected'
|
|
});
|
|
}
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ad/users:
|
|
* get:
|
|
* summary: Get Active Directory users
|
|
* description: Retrieve users from Active Directory
|
|
* tags: [Active Directory]
|
|
* parameters:
|
|
* - in: query
|
|
* name: connectionId
|
|
* schema:
|
|
* type: integer
|
|
* required: true
|
|
* description: LDAP Connection ID
|
|
* - in: query
|
|
* name: filter
|
|
* schema:
|
|
* type: string
|
|
* description: LDAP filter expression
|
|
* - in: query
|
|
* name: properties
|
|
* schema:
|
|
* type: string
|
|
* description: Comma-separated list of properties to return
|
|
* responses:
|
|
* 200:
|
|
* description: A list of users
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/LdapUser'
|
|
*/
|
|
router.get('/ad/users', verifyApiToken, async (req, res) => {
|
|
try {
|
|
const connectionId = parseInt(req.query.connectionId as string);
|
|
if (isNaN(connectionId)) {
|
|
return res.status(400).json({ message: 'Invalid connection ID' });
|
|
}
|
|
|
|
const connection = await storage.getLdapConnection(connectionId);
|
|
if (!connection) {
|
|
return res.status(404).json({ message: 'Connection not found' });
|
|
}
|
|
|
|
// Connect if not already connected
|
|
if (!ldapClient.isConnectionActive(connectionId)) {
|
|
try {
|
|
await ldapClient.connect(connection);
|
|
} catch (error) {
|
|
return res.status(500).json({
|
|
message: 'Failed to connect to LDAP server',
|
|
error: error instanceof Error ? error.message : String(error)
|
|
});
|
|
}
|
|
}
|
|
|
|
const filter = req.query.filter as string || '(objectClass=user)';
|
|
const propertiesParam = req.query.properties as string;
|
|
const properties = propertiesParam ? propertiesParam.split(',') : undefined;
|
|
|
|
const users = await ldapClient.searchUsers(connectionId, filter, properties);
|
|
|
|
res.json(users);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ad/groups:
|
|
* get:
|
|
* summary: Get Active Directory groups
|
|
* description: Retrieve groups from Active Directory
|
|
* tags: [Active Directory]
|
|
* parameters:
|
|
* - in: query
|
|
* name: connectionId
|
|
* schema:
|
|
* type: integer
|
|
* required: true
|
|
* description: LDAP Connection ID
|
|
* - in: query
|
|
* name: filter
|
|
* schema:
|
|
* type: string
|
|
* description: LDAP filter expression
|
|
* - in: query
|
|
* name: properties
|
|
* schema:
|
|
* type: string
|
|
* description: Comma-separated list of properties to return
|
|
* responses:
|
|
* 200:
|
|
* description: A list of groups
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/LdapGroup'
|
|
*/
|
|
router.get('/ad/groups', verifyApiToken, async (req, res) => {
|
|
try {
|
|
const connectionId = parseInt(req.query.connectionId as string);
|
|
if (isNaN(connectionId)) {
|
|
return res.status(400).json({ message: 'Invalid connection ID' });
|
|
}
|
|
|
|
const connection = await storage.getLdapConnection(connectionId);
|
|
if (!connection) {
|
|
return res.status(404).json({ message: 'Connection not found' });
|
|
}
|
|
|
|
// Connect if not already connected
|
|
if (!ldapClient.isConnectionActive(connectionId)) {
|
|
try {
|
|
await ldapClient.connect(connection);
|
|
} catch (error) {
|
|
return res.status(500).json({
|
|
message: 'Failed to connect to LDAP server',
|
|
error: error instanceof Error ? error.message : String(error)
|
|
});
|
|
}
|
|
}
|
|
|
|
const filter = req.query.filter as string || '(objectClass=group)';
|
|
const propertiesParam = req.query.properties as string;
|
|
const properties = propertiesParam ? propertiesParam.split(',') : undefined;
|
|
|
|
const groups = await ldapClient.searchGroups(connectionId, filter, properties);
|
|
|
|
res.json(groups);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ad/organizational-units:
|
|
* get:
|
|
* summary: Get Active Directory organizational units
|
|
* description: Retrieve organizational units from Active Directory
|
|
* tags: [Active Directory]
|
|
* parameters:
|
|
* - in: query
|
|
* name: connectionId
|
|
* schema:
|
|
* type: integer
|
|
* required: true
|
|
* description: LDAP Connection ID
|
|
* - in: query
|
|
* name: filter
|
|
* schema:
|
|
* type: string
|
|
* description: LDAP filter expression
|
|
* - in: query
|
|
* name: properties
|
|
* schema:
|
|
* type: string
|
|
* description: Comma-separated list of properties to return
|
|
* responses:
|
|
* 200:
|
|
* description: A list of organizational units
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/LdapOU'
|
|
*/
|
|
router.get('/ad/organizational-units', verifyApiToken, async (req, res) => {
|
|
try {
|
|
const connectionId = parseInt(req.query.connectionId as string);
|
|
if (isNaN(connectionId)) {
|
|
return res.status(400).json({ message: 'Invalid connection ID' });
|
|
}
|
|
|
|
const connection = await storage.getLdapConnection(connectionId);
|
|
if (!connection) {
|
|
return res.status(404).json({ message: 'Connection not found' });
|
|
}
|
|
|
|
// Connect if not already connected
|
|
if (!ldapClient.isConnectionActive(connectionId)) {
|
|
try {
|
|
await ldapClient.connect(connection);
|
|
} catch (error) {
|
|
return res.status(500).json({
|
|
message: 'Failed to connect to LDAP server',
|
|
error: error instanceof Error ? error.message : String(error)
|
|
});
|
|
}
|
|
}
|
|
|
|
const filter = req.query.filter as string || '(objectClass=organizationalUnit)';
|
|
const propertiesParam = req.query.properties as string;
|
|
const properties = propertiesParam ? propertiesParam.split(',') : undefined;
|
|
|
|
const ous = await ldapClient.searchOUs(connectionId, filter, properties);
|
|
|
|
res.json(ous);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /ad/computers:
|
|
* get:
|
|
* summary: Get Active Directory computers
|
|
* description: Retrieve computers from Active Directory
|
|
* tags: [Active Directory]
|
|
* parameters:
|
|
* - in: query
|
|
* name: connectionId
|
|
* schema:
|
|
* type: integer
|
|
* required: true
|
|
* description: LDAP Connection ID
|
|
* - in: query
|
|
* name: filter
|
|
* schema:
|
|
* type: string
|
|
* description: LDAP filter expression
|
|
* - in: query
|
|
* name: properties
|
|
* schema:
|
|
* type: string
|
|
* description: Comma-separated list of properties to return
|
|
* responses:
|
|
* 200:
|
|
* description: A list of computers
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/LdapComputer'
|
|
*/
|
|
router.get('/ad/computers', verifyApiToken, async (req, res) => {
|
|
try {
|
|
const connectionId = parseInt(req.query.connectionId as string);
|
|
if (isNaN(connectionId)) {
|
|
return res.status(400).json({ message: 'Invalid connection ID' });
|
|
}
|
|
|
|
const connection = await storage.getLdapConnection(connectionId);
|
|
if (!connection) {
|
|
return res.status(404).json({ message: 'Connection not found' });
|
|
}
|
|
|
|
// Connect if not already connected
|
|
if (!ldapClient.isConnectionActive(connectionId)) {
|
|
try {
|
|
await ldapClient.connect(connection);
|
|
} catch (error) {
|
|
return res.status(500).json({
|
|
message: 'Failed to connect to LDAP server',
|
|
error: error instanceof Error ? error.message : String(error)
|
|
});
|
|
}
|
|
}
|
|
|
|
const filter = req.query.filter as string || '(objectClass=computer)';
|
|
const propertiesParam = req.query.properties as string;
|
|
const properties = propertiesParam ? propertiesParam.split(',') : undefined;
|
|
|
|
const computers = await ldapClient.searchComputers(connectionId, filter, properties);
|
|
|
|
res.json(computers);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /activity-logs:
|
|
* get:
|
|
* summary: Get activity logs
|
|
* description: Retrieve a list of recent activity logs
|
|
* tags: [Activity Logs]
|
|
* parameters:
|
|
* - in: query
|
|
* name: limit
|
|
* schema:
|
|
* type: integer
|
|
* description: Maximum number of logs to return
|
|
* responses:
|
|
* 200:
|
|
* description: A list of activity logs
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: array
|
|
* items:
|
|
* $ref: '#/components/schemas/ActivityLog'
|
|
*/
|
|
router.get('/activity-logs', async (req, res) => {
|
|
try {
|
|
if (!req.isAuthenticated()) {
|
|
return res.status(401).json({ message: 'Not authenticated' });
|
|
}
|
|
|
|
const limit = req.query.limit ? parseInt(req.query.limit as string) : 10;
|
|
|
|
const logs = await storage.listActivityLogs(limit);
|
|
|
|
res.json(logs);
|
|
} catch (error) {
|
|
res.status(500).json({ message: error instanceof Error ? error.message : 'An error occurred' });
|
|
}
|
|
});
|
|
}
|