mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-19 14:57:03 +00:00
Add ability to update manager for Active Directory users.
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/4557185a-6c8f-4519-939e-2acdaebd1657.jpg
This commit is contained in:
+406
-279
@@ -11,7 +11,6 @@ import {
|
|||||||
moveUserSchema,
|
moveUserSchema,
|
||||||
addToGroupSchema,
|
addToGroupSchema,
|
||||||
removeFromGroupSchema,
|
removeFromGroupSchema,
|
||||||
updateManagedBySchema,
|
|
||||||
adUsers,
|
adUsers,
|
||||||
adGroups,
|
adGroups,
|
||||||
adOrgUnits,
|
adOrgUnits,
|
||||||
@@ -831,6 +830,105 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/connections/{connectionId}/ad-users/{id}/managed-by:
|
||||||
|
* patch:
|
||||||
|
* summary: Update the managedBy attribute for 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:
|
||||||
|
* managerDistinguishedName:
|
||||||
|
* type: string
|
||||||
|
* nullable: true
|
||||||
|
* description: The distinguished name of the manager, or null to remove the manager
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The updated AD user
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/AdUser'
|
||||||
|
* 401:
|
||||||
|
* $ref: '#/components/responses/UnauthorizedError'
|
||||||
|
* 404:
|
||||||
|
* description: User not found
|
||||||
|
*/
|
||||||
|
app.patch("/api/connections/:connectionId/ad-users/:id/managed-by", authenticateApiToken, async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const connectionId = parseInt(req.params.connectionId);
|
||||||
|
const userId = parseInt(req.params.id);
|
||||||
|
const { managerDistinguishedName } = req.body;
|
||||||
|
|
||||||
|
// Get the connection
|
||||||
|
const connection = await storage.getLdapConnection(connectionId);
|
||||||
|
if (!connection) {
|
||||||
|
return res.status(404).json({ message: "LDAP connection not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the user
|
||||||
|
const user = await storage.getAdUser(userId);
|
||||||
|
if (!user || user.connectionId !== connectionId) {
|
||||||
|
return res.status(404).json({ message: "AD user not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to LDAP
|
||||||
|
try {
|
||||||
|
await ldapClient.connect(connection);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("LDAP connection error:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to connect to LDAP server", error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Set the managedBy attribute
|
||||||
|
await ldapClient.setManagedBy(connectionId, user.distinguishedName, managerDistinguishedName);
|
||||||
|
|
||||||
|
// Update the user in the database
|
||||||
|
const updatedUser = await storage.updateAdUser(userId, { managedBy: managerDistinguishedName });
|
||||||
|
|
||||||
|
// Log the action
|
||||||
|
await storage.createAuditLogEntry({
|
||||||
|
action: managerDistinguishedName ? "update_manager" : "remove_manager",
|
||||||
|
targetId: user.objectGUID,
|
||||||
|
details: {
|
||||||
|
objectType: "user",
|
||||||
|
managerDN: managerDistinguishedName,
|
||||||
|
userDN: user.distinguishedName
|
||||||
|
},
|
||||||
|
userId: req.user?.id,
|
||||||
|
connectionId
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(updatedUser);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating managedBy attribute:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to update managedBy attribute", error: error.message });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/connections/{connectionId}/ad-users/{id}:
|
* /api/connections/{connectionId}/ad-users/{id}:
|
||||||
@@ -953,6 +1051,105 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/connections/{connectionId}/ad-groups/{id}/managed-by:
|
||||||
|
* patch:
|
||||||
|
* summary: Update the managedBy attribute for 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:
|
||||||
|
* managerDistinguishedName:
|
||||||
|
* type: string
|
||||||
|
* nullable: true
|
||||||
|
* description: The distinguished name of the manager, or null to remove the manager
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The updated AD group
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/AdGroup'
|
||||||
|
* 401:
|
||||||
|
* $ref: '#/components/responses/UnauthorizedError'
|
||||||
|
* 404:
|
||||||
|
* description: Group not found
|
||||||
|
*/
|
||||||
|
app.patch("/api/connections/:connectionId/ad-groups/:id/managed-by", authenticateApiToken, async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const connectionId = parseInt(req.params.connectionId);
|
||||||
|
const groupId = parseInt(req.params.id);
|
||||||
|
const { managerDistinguishedName } = req.body;
|
||||||
|
|
||||||
|
// Get the connection
|
||||||
|
const connection = await storage.getLdapConnection(connectionId);
|
||||||
|
if (!connection) {
|
||||||
|
return res.status(404).json({ message: "LDAP connection not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the group
|
||||||
|
const group = await storage.getAdGroup(groupId);
|
||||||
|
if (!group || group.connectionId !== connectionId) {
|
||||||
|
return res.status(404).json({ message: "AD group not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to LDAP
|
||||||
|
try {
|
||||||
|
await ldapClient.connect(connection);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("LDAP connection error:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to connect to LDAP server", error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Set the managedBy attribute
|
||||||
|
await ldapClient.setManagedBy(connectionId, group.distinguishedName, managerDistinguishedName);
|
||||||
|
|
||||||
|
// Update the group in the database
|
||||||
|
const updatedGroup = await storage.updateAdGroup(groupId, { managedBy: managerDistinguishedName });
|
||||||
|
|
||||||
|
// Log the action
|
||||||
|
await storage.createAuditLogEntry({
|
||||||
|
action: managerDistinguishedName ? "update_manager" : "remove_manager",
|
||||||
|
targetId: group.objectGUID,
|
||||||
|
details: {
|
||||||
|
objectType: "group",
|
||||||
|
managerDN: managerDistinguishedName,
|
||||||
|
groupDN: group.distinguishedName
|
||||||
|
},
|
||||||
|
userId: req.user?.id,
|
||||||
|
connectionId
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(updatedGroup);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating managedBy attribute:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to update managedBy attribute", error: error.message });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/connections/{connectionId}/ad-groups/{id}:
|
* /api/connections/{connectionId}/ad-groups/{id}:
|
||||||
@@ -1307,6 +1504,105 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/connections/{connectionId}/ad-org-units/{id}/managed-by:
|
||||||
|
* patch:
|
||||||
|
* summary: Update the managedBy attribute for 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:
|
||||||
|
* managerDistinguishedName:
|
||||||
|
* type: string
|
||||||
|
* nullable: true
|
||||||
|
* description: The distinguished name of the manager, or null to remove the manager
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The updated AD organizational unit
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/AdOrgUnit'
|
||||||
|
* 401:
|
||||||
|
* $ref: '#/components/responses/UnauthorizedError'
|
||||||
|
* 404:
|
||||||
|
* description: Organizational unit not found
|
||||||
|
*/
|
||||||
|
app.patch("/api/connections/:connectionId/ad-org-units/:id/managed-by", authenticateApiToken, async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const connectionId = parseInt(req.params.connectionId);
|
||||||
|
const ouId = parseInt(req.params.id);
|
||||||
|
const { managerDistinguishedName } = req.body;
|
||||||
|
|
||||||
|
// Get the connection
|
||||||
|
const connection = await storage.getLdapConnection(connectionId);
|
||||||
|
if (!connection) {
|
||||||
|
return res.status(404).json({ message: "LDAP connection not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the OU
|
||||||
|
const ou = await storage.getAdOrgUnit(ouId);
|
||||||
|
if (!ou || ou.connectionId !== connectionId) {
|
||||||
|
return res.status(404).json({ message: "AD organizational unit not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to LDAP
|
||||||
|
try {
|
||||||
|
await ldapClient.connect(connection);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("LDAP connection error:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to connect to LDAP server", error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Set the managedBy attribute
|
||||||
|
await ldapClient.setManagedBy(connectionId, ou.distinguishedName, managerDistinguishedName);
|
||||||
|
|
||||||
|
// Update the OU in the database
|
||||||
|
const updatedOU = await storage.updateAdOrgUnit(ouId, { managedBy: managerDistinguishedName });
|
||||||
|
|
||||||
|
// Log the action
|
||||||
|
await storage.createAuditLogEntry({
|
||||||
|
action: managerDistinguishedName ? "update_manager" : "remove_manager",
|
||||||
|
targetId: ou.objectGUID,
|
||||||
|
details: {
|
||||||
|
objectType: "organizationalUnit",
|
||||||
|
managerDN: managerDistinguishedName,
|
||||||
|
ouDN: ou.distinguishedName
|
||||||
|
},
|
||||||
|
userId: req.user?.id,
|
||||||
|
connectionId
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(updatedOU);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating managedBy attribute:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to update managedBy attribute", error: error.message });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/connections/{connectionId}/ad-org-units/{id}:
|
* /api/connections/{connectionId}/ad-org-units/{id}:
|
||||||
@@ -1525,6 +1821,105 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/connections/{connectionId}/ad-computers/{id}/managed-by:
|
||||||
|
* patch:
|
||||||
|
* summary: Update the managedBy attribute for 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:
|
||||||
|
* managerDistinguishedName:
|
||||||
|
* type: string
|
||||||
|
* nullable: true
|
||||||
|
* description: The distinguished name of the manager, or null to remove the manager
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: The updated AD computer
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/AdComputer'
|
||||||
|
* 401:
|
||||||
|
* $ref: '#/components/responses/UnauthorizedError'
|
||||||
|
* 404:
|
||||||
|
* description: Computer not found
|
||||||
|
*/
|
||||||
|
app.patch("/api/connections/:connectionId/ad-computers/:id/managed-by", authenticateApiToken, async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const connectionId = parseInt(req.params.connectionId);
|
||||||
|
const computerId = parseInt(req.params.id);
|
||||||
|
const { managerDistinguishedName } = req.body;
|
||||||
|
|
||||||
|
// Get the connection
|
||||||
|
const connection = await storage.getLdapConnection(connectionId);
|
||||||
|
if (!connection) {
|
||||||
|
return res.status(404).json({ message: "LDAP connection not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the computer
|
||||||
|
const computer = await storage.getAdComputer(computerId);
|
||||||
|
if (!computer || computer.connectionId !== connectionId) {
|
||||||
|
return res.status(404).json({ message: "AD computer not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to LDAP
|
||||||
|
try {
|
||||||
|
await ldapClient.connect(connection);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("LDAP connection error:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to connect to LDAP server", error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Set the managedBy attribute
|
||||||
|
await ldapClient.setManagedBy(connectionId, computer.distinguishedName, managerDistinguishedName);
|
||||||
|
|
||||||
|
// Update the computer in the database
|
||||||
|
const updatedComputer = await storage.updateAdComputer(computerId, { managedBy: managerDistinguishedName });
|
||||||
|
|
||||||
|
// Log the action
|
||||||
|
await storage.createAuditLogEntry({
|
||||||
|
action: managerDistinguishedName ? "update_manager" : "remove_manager",
|
||||||
|
targetId: computer.objectGUID,
|
||||||
|
details: {
|
||||||
|
objectType: "computer",
|
||||||
|
managerDN: managerDistinguishedName,
|
||||||
|
computerDN: computer.distinguishedName
|
||||||
|
},
|
||||||
|
userId: req.user?.id,
|
||||||
|
connectionId
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(updatedComputer);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating managedBy attribute:", error);
|
||||||
|
return res.status(500).json({ message: "Failed to update managedBy attribute", error: error.message });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/connections/{connectionId}/ad-computers/{id}:
|
* /api/connections/{connectionId}/ad-computers/{id}:
|
||||||
@@ -3297,285 +3692,17 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
// This dedicated API endpoint has been replaced by object-specific endpoints using the PATCH method
|
||||||
* @swagger
|
// See: /api/connections/:connectionId/ad-users/:id/managed-by,
|
||||||
* /api/connections/{connectionId}/managed-by:
|
// /api/connections/:connectionId/ad-groups/:id/managed-by,
|
||||||
* get:
|
// /api/connections/:connectionId/ad-computers/:id/managed-by,
|
||||||
* summary: Get the 'managedBy' attribute for an AD object
|
// /api/connections/:connectionId/ad-org-units/:id/managed-by
|
||||||
* tags: [AD Objects]
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* - cookieAuth: []
|
|
||||||
* parameters:
|
|
||||||
* - name: connectionId
|
|
||||||
* in: path
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: integer
|
|
||||||
* - name: objectGUID
|
|
||||||
* in: query
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* - name: objectType
|
|
||||||
* in: query
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: string
|
|
||||||
* enum: [user, group, computer, organizationalUnit]
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: The managedBy attribute
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* managedBy:
|
|
||||||
* type: string
|
|
||||||
* nullable: true
|
|
||||||
* 401:
|
|
||||||
* $ref: '#/components/responses/UnauthorizedError'
|
|
||||||
* 404:
|
|
||||||
* description: Object not found
|
|
||||||
*/
|
|
||||||
app.get("/api/connections/:connectionId/managed-by", authenticateApiToken, async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const connectionId = parseInt(req.params.connectionId);
|
|
||||||
const connection = await storage.getLdapConnection(connectionId);
|
|
||||||
|
|
||||||
if (!connection) {
|
// This dedicated API endpoint has been replaced by object-specific endpoints using the PATCH method
|
||||||
return res.status(404).json({ message: "LDAP connection not found" });
|
// See: /api/connections/:connectionId/ad-users/:objectGuid,
|
||||||
}
|
// /api/connections/:connectionId/ad-groups/:objectGuid,
|
||||||
|
// /api/connections/:connectionId/ad-computers/:objectGuid,
|
||||||
const objectGUID = req.query.objectGUID as string;
|
// /api/connections/:connectionId/ad-org-units/:objectGuid
|
||||||
const objectType = req.query.objectType as string;
|
|
||||||
|
|
||||||
if (!objectGUID || !objectType) {
|
|
||||||
return res.status(400).json({ message: "objectGUID and objectType are required query parameters" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!['user', 'group', 'computer', 'organizationalUnit'].includes(objectType)) {
|
|
||||||
return res.status(400).json({ message: "objectType must be one of: user, group, computer, organizationalUnit" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the object distinguished name
|
|
||||||
let objectDN;
|
|
||||||
switch (objectType) {
|
|
||||||
case 'user':
|
|
||||||
const adUser = await storage.getAdUserByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adUser) {
|
|
||||||
return res.status(404).json({ message: "User not found" });
|
|
||||||
}
|
|
||||||
objectDN = adUser.distinguishedName;
|
|
||||||
break;
|
|
||||||
case 'group':
|
|
||||||
const adGroup = await storage.getAdGroupByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adGroup) {
|
|
||||||
return res.status(404).json({ message: "Group not found" });
|
|
||||||
}
|
|
||||||
objectDN = adGroup.distinguishedName;
|
|
||||||
break;
|
|
||||||
case 'computer':
|
|
||||||
const adComputer = await storage.getAdComputerByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adComputer) {
|
|
||||||
return res.status(404).json({ message: "Computer not found" });
|
|
||||||
}
|
|
||||||
objectDN = adComputer.distinguishedName;
|
|
||||||
break;
|
|
||||||
case 'organizationalUnit':
|
|
||||||
const adOU = await storage.getAdOrgUnitByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adOU) {
|
|
||||||
return res.status(404).json({ message: "Organizational Unit not found" });
|
|
||||||
}
|
|
||||||
objectDN = adOU.distinguishedName;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to LDAP
|
|
||||||
try {
|
|
||||||
await ldapClient.connect(connection);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("LDAP connection error:", error);
|
|
||||||
return res.status(500).json({ message: "Failed to connect to LDAP server", error: error.message });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get the managedBy attribute
|
|
||||||
const managedBy = await ldapClient.getManagedBy(connectionId, objectDN);
|
|
||||||
|
|
||||||
return res.status(200).json({ managedBy });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error getting managedBy attribute:", error);
|
|
||||||
return res.status(500).json({ message: "Failed to get managedBy attribute", error: error.message });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @swagger
|
|
||||||
* /api/connections/{connectionId}/managed-by:
|
|
||||||
* post:
|
|
||||||
* summary: Set the 'managedBy' attribute for an AD object
|
|
||||||
* tags: [AD Objects]
|
|
||||||
* security:
|
|
||||||
* - bearerAuth: []
|
|
||||||
* - cookieAuth: []
|
|
||||||
* parameters:
|
|
||||||
* - name: connectionId
|
|
||||||
* in: path
|
|
||||||
* required: true
|
|
||||||
* schema:
|
|
||||||
* type: integer
|
|
||||||
* requestBody:
|
|
||||||
* required: true
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* required:
|
|
||||||
* - objectGUID
|
|
||||||
* - objectType
|
|
||||||
* properties:
|
|
||||||
* objectGUID:
|
|
||||||
* type: string
|
|
||||||
* managerDistinguishedName:
|
|
||||||
* type: string
|
|
||||||
* nullable: true
|
|
||||||
* objectType:
|
|
||||||
* type: string
|
|
||||||
* enum: [user, group, computer, organizationalUnit]
|
|
||||||
* responses:
|
|
||||||
* 200:
|
|
||||||
* description: ManagedBy attribute updated successfully
|
|
||||||
* content:
|
|
||||||
* application/json:
|
|
||||||
* schema:
|
|
||||||
* type: object
|
|
||||||
* properties:
|
|
||||||
* success:
|
|
||||||
* type: boolean
|
|
||||||
* message:
|
|
||||||
* type: string
|
|
||||||
* 401:
|
|
||||||
* $ref: '#/components/responses/UnauthorizedError'
|
|
||||||
* 404:
|
|
||||||
* description: Object not found
|
|
||||||
*/
|
|
||||||
app.post("/api/connections/:connectionId/managed-by", authenticateApiToken, async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
// Parse and validate request body
|
|
||||||
try {
|
|
||||||
updateManagedBySchema.parse(req.body);
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof ZodError) {
|
|
||||||
return handleZodError(err, res);
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 { objectGUID, managerDistinguishedName, objectType } = req.body;
|
|
||||||
|
|
||||||
// Get the object distinguished name
|
|
||||||
let objectDN;
|
|
||||||
let objectName;
|
|
||||||
switch (objectType) {
|
|
||||||
case 'user':
|
|
||||||
const adUser = await storage.getAdUserByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adUser) {
|
|
||||||
return res.status(404).json({ message: "User not found" });
|
|
||||||
}
|
|
||||||
objectDN = adUser.distinguishedName;
|
|
||||||
objectName = adUser.displayName || adUser.sAMAccountName;
|
|
||||||
break;
|
|
||||||
case 'group':
|
|
||||||
const adGroup = await storage.getAdGroupByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adGroup) {
|
|
||||||
return res.status(404).json({ message: "Group not found" });
|
|
||||||
}
|
|
||||||
objectDN = adGroup.distinguishedName;
|
|
||||||
objectName = adGroup.sAMAccountName;
|
|
||||||
break;
|
|
||||||
case 'computer':
|
|
||||||
const adComputer = await storage.getAdComputerByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adComputer) {
|
|
||||||
return res.status(404).json({ message: "Computer not found" });
|
|
||||||
}
|
|
||||||
objectDN = adComputer.distinguishedName;
|
|
||||||
objectName = adComputer.name;
|
|
||||||
break;
|
|
||||||
case 'organizationalUnit':
|
|
||||||
const adOU = await storage.getAdOrgUnitByObjectGUID(connectionId, objectGUID);
|
|
||||||
if (!adOU) {
|
|
||||||
return res.status(404).json({ message: "Organizational Unit not found" });
|
|
||||||
}
|
|
||||||
objectDN = adOU.distinguishedName;
|
|
||||||
objectName = adOU.name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to LDAP
|
|
||||||
try {
|
|
||||||
await ldapClient.connect(connection);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("LDAP connection error:", error);
|
|
||||||
return res.status(500).json({ message: "Failed to connect to LDAP server", error: error.message });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Set the managedBy attribute
|
|
||||||
await ldapClient.setManagedBy(connectionId, objectDN, managerDistinguishedName);
|
|
||||||
|
|
||||||
// Update the database record
|
|
||||||
const updateData = { managedBy: managerDistinguishedName };
|
|
||||||
switch (objectType) {
|
|
||||||
case 'user':
|
|
||||||
await storage.updateAdUserByObjectGUID(connectionId, objectGUID, updateData);
|
|
||||||
break;
|
|
||||||
case 'group':
|
|
||||||
await storage.updateAdGroupByObjectGUID(connectionId, objectGUID, updateData);
|
|
||||||
break;
|
|
||||||
case 'computer':
|
|
||||||
await storage.updateAdComputerByObjectGUID(connectionId, objectGUID, updateData);
|
|
||||||
break;
|
|
||||||
case 'organizationalUnit':
|
|
||||||
await storage.updateAdOrgUnitByObjectGUID(connectionId, objectGUID, updateData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the action
|
|
||||||
await storage.createAuditLogEntry({
|
|
||||||
action: `update_managed_by:${objectType}`,
|
|
||||||
targetId: objectGUID,
|
|
||||||
details: {
|
|
||||||
objectDN,
|
|
||||||
managerDN: managerDistinguishedName
|
|
||||||
},
|
|
||||||
userId: req.user?.id,
|
|
||||||
connectionId
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).json({
|
|
||||||
success: true,
|
|
||||||
message: `ManagedBy attribute successfully updated for ${objectName}`
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating managedBy attribute:", error);
|
|
||||||
return res.status(500).json({ message: "Failed to update managedBy attribute", error: error.message });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
|
|||||||
+4
-2
@@ -982,7 +982,7 @@ export class DatabaseStorage implements IStorage {
|
|||||||
debug(`Getting audit logs: connectionId=${connectionId}, userId=${userId}, page=${page}, pageSize=${pageSize}`);
|
debug(`Getting audit logs: connectionId=${connectionId}, userId=${userId}, page=${page}, pageSize=${pageSize}`);
|
||||||
|
|
||||||
// Define the base query for counting total records
|
// Define the base query for counting total records
|
||||||
let countQuery = db.select({ count: sql`count(*)` }).from(auditLogs);
|
let countQuery = db.select({ count: sql`count(*)::int` }).from(auditLogs);
|
||||||
let dataQuery = db.select().from(auditLogs);
|
let dataQuery = db.select().from(auditLogs);
|
||||||
|
|
||||||
// Apply filters if provided
|
// Apply filters if provided
|
||||||
@@ -1005,7 +1005,9 @@ export class DatabaseStorage implements IStorage {
|
|||||||
|
|
||||||
// Get total count of records
|
// Get total count of records
|
||||||
const countResult = await countQuery;
|
const countResult = await countQuery;
|
||||||
const totalRecords = parseInt(countResult[0].count.toString());
|
const totalRecords = typeof countResult[0].count === 'number'
|
||||||
|
? countResult[0].count
|
||||||
|
: parseInt(String(countResult[0].count), 10);
|
||||||
|
|
||||||
// Calculate pagination values
|
// Calculate pagination values
|
||||||
const totalPages = Math.ceil(totalRecords / pageSize);
|
const totalPages = Math.ceil(totalRecords / pageSize);
|
||||||
|
|||||||
+2
-7
@@ -311,12 +311,7 @@ export const removeFromGroupSchema = z.object({
|
|||||||
objectType: z.enum(["user", "computer"]),
|
objectType: z.enum(["user", "computer"]),
|
||||||
});
|
});
|
||||||
|
|
||||||
// ManagedBy operation schema
|
// ManagedBy operation schema is no longer needed as it's been replaced by object-specific PATCH endpoints
|
||||||
export const updateManagedBySchema = z.object({
|
|
||||||
objectGUID: z.string().min(1, "Object GUID is required"),
|
|
||||||
managerDistinguishedName: z.string().nullable(),
|
|
||||||
objectType: z.enum(["user", "group", "computer", "organizationalUnit"]),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Export types
|
// Export types
|
||||||
export type Role = typeof roles.$inferSelect;
|
export type Role = typeof roles.$inferSelect;
|
||||||
@@ -350,7 +345,7 @@ export type MoveComputer = z.infer<typeof moveComputerSchema>;
|
|||||||
export type MoveUser = z.infer<typeof moveUserSchema>;
|
export type MoveUser = z.infer<typeof moveUserSchema>;
|
||||||
export type AddToGroup = z.infer<typeof addToGroupSchema>;
|
export type AddToGroup = z.infer<typeof addToGroupSchema>;
|
||||||
export type RemoveFromGroup = z.infer<typeof removeFromGroupSchema>;
|
export type RemoveFromGroup = z.infer<typeof removeFromGroupSchema>;
|
||||||
export type UpdateManagedBy = z.infer<typeof updateManagedBySchema>;
|
|
||||||
|
|
||||||
// LDAP Query Builder schemas
|
// LDAP Query Builder schemas
|
||||||
export const ldapFilterObjectClasses = ["user", "group", "organizationalUnit", "computer", "domain"] as const;
|
export const ldapFilterObjectClasses = ["user", "group", "organizationalUnit", "computer", "domain"] as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user