mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-24 17:46:52 +00:00
Add ability to manage the 'managedBy' attribute for AD objects
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/2b2008cc-a510-433c-9379-89ffac9ce6a0.jpg
This commit is contained in:
@@ -196,6 +196,75 @@ class LdapClient extends EventEmitter {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the managed by attribute for an object
|
||||||
|
async getManagedBy(connectionId: number, dn: string): Promise<string | null> {
|
||||||
|
const client = this.getClient(connectionId);
|
||||||
|
if (!client) throw new Error('LDAP connection not established');
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
client.search(dn, {
|
||||||
|
scope: 'base',
|
||||||
|
attributes: ['managedBy']
|
||||||
|
}, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let managedBy: string | null = null;
|
||||||
|
|
||||||
|
res.on('searchEntry', (entry) => {
|
||||||
|
const attrs = entry.attributes;
|
||||||
|
for (const attr of attrs) {
|
||||||
|
if (attr.type === 'managedBy' && attr.vals && attr.vals.length > 0) {
|
||||||
|
managedBy = attr.vals[0].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', (result) => {
|
||||||
|
if (result.status !== 0) {
|
||||||
|
reject(new Error(`LDAP search error: ${result.errorMessage}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(managedBy);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the managed by attribute for an object
|
||||||
|
async setManagedBy(connectionId: number, dn: string, managerDn: string | null): Promise<boolean> {
|
||||||
|
const client = this.getClient(connectionId);
|
||||||
|
if (!client) throw new Error('LDAP connection not established');
|
||||||
|
|
||||||
|
const changes = [];
|
||||||
|
|
||||||
|
if (managerDn === null) {
|
||||||
|
// Remove the managedBy attribute
|
||||||
|
changes.push(new ldap.Change({
|
||||||
|
operation: 'delete',
|
||||||
|
modification: {
|
||||||
|
managedBy: []
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// Add or replace the managedBy attribute
|
||||||
|
changes.push(new ldap.Change({
|
||||||
|
operation: 'replace',
|
||||||
|
modification: {
|
||||||
|
managedBy: managerDn
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.updateEntry(connectionId, dn, changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ldapClient = new LdapClient();
|
export const ldapClient = new LdapClient();
|
||||||
|
|||||||
+282
-1
@@ -9,7 +9,8 @@ import {
|
|||||||
moveComputerSchema,
|
moveComputerSchema,
|
||||||
moveUserSchema,
|
moveUserSchema,
|
||||||
addToGroupSchema,
|
addToGroupSchema,
|
||||||
removeFromGroupSchema
|
removeFromGroupSchema,
|
||||||
|
updateManagedBySchema
|
||||||
} from "@shared/schema";
|
} from "@shared/schema";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
import rateLimit from "express-rate-limit";
|
import rateLimit from "express-rate-limit";
|
||||||
@@ -3083,6 +3084,286 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/connections/{connectionId}/managed-by:
|
||||||
|
* get:
|
||||||
|
* summary: Get the 'managedBy' attribute for an AD object
|
||||||
|
* 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) {
|
||||||
|
return res.status(404).json({ message: "LDAP connection not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectGUID = req.query.objectGUID as string;
|
||||||
|
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
|
||||||
* /api/audit-logs:
|
* /api/audit-logs:
|
||||||
|
|||||||
@@ -80,29 +80,37 @@ export interface IStorage {
|
|||||||
|
|
||||||
// AD Users
|
// AD Users
|
||||||
getAdUser(id: number): Promise<AdUser | undefined>;
|
getAdUser(id: number): Promise<AdUser | undefined>;
|
||||||
|
getAdUserByObjectGUID(connectionId: number, objectGUID: string): Promise<AdUser | undefined>;
|
||||||
createAdUser(user: InsertAdUser): Promise<AdUser>;
|
createAdUser(user: InsertAdUser): Promise<AdUser>;
|
||||||
updateAdUser(id: number, user: Partial<AdUser>): Promise<AdUser | undefined>;
|
updateAdUser(id: number, user: Partial<AdUser>): Promise<AdUser | undefined>;
|
||||||
|
updateAdUserByObjectGUID(connectionId: number, objectGUID: string, user: Partial<AdUser>): Promise<AdUser | undefined>;
|
||||||
deleteAdUser(id: number): Promise<boolean>;
|
deleteAdUser(id: number): Promise<boolean>;
|
||||||
listAdUsers(connectionId: number, query?: any): Promise<AdUser[]>;
|
listAdUsers(connectionId: number, query?: any): Promise<AdUser[]>;
|
||||||
|
|
||||||
// AD Groups
|
// AD Groups
|
||||||
getAdGroup(id: number): Promise<AdGroup | undefined>;
|
getAdGroup(id: number): Promise<AdGroup | undefined>;
|
||||||
|
getAdGroupByObjectGUID(connectionId: number, objectGUID: string): Promise<AdGroup | undefined>;
|
||||||
createAdGroup(group: InsertAdGroup): Promise<AdGroup>;
|
createAdGroup(group: InsertAdGroup): Promise<AdGroup>;
|
||||||
updateAdGroup(id: number, group: Partial<AdGroup>): Promise<AdGroup | undefined>;
|
updateAdGroup(id: number, group: Partial<AdGroup>): Promise<AdGroup | undefined>;
|
||||||
|
updateAdGroupByObjectGUID(connectionId: number, objectGUID: string, group: Partial<AdGroup>): Promise<AdGroup | undefined>;
|
||||||
deleteAdGroup(id: number): Promise<boolean>;
|
deleteAdGroup(id: number): Promise<boolean>;
|
||||||
listAdGroups(connectionId: number, query?: any): Promise<AdGroup[]>;
|
listAdGroups(connectionId: number, query?: any): Promise<AdGroup[]>;
|
||||||
|
|
||||||
// AD Organizational Units
|
// AD Organizational Units
|
||||||
getAdOrgUnit(id: number): Promise<AdOrgUnit | undefined>;
|
getAdOrgUnit(id: number): Promise<AdOrgUnit | undefined>;
|
||||||
|
getAdOrgUnitByObjectGUID(connectionId: number, objectGUID: string): Promise<AdOrgUnit | undefined>;
|
||||||
createAdOrgUnit(ou: InsertAdOrgUnit): Promise<AdOrgUnit>;
|
createAdOrgUnit(ou: InsertAdOrgUnit): Promise<AdOrgUnit>;
|
||||||
updateAdOrgUnit(id: number, ou: Partial<AdOrgUnit>): Promise<AdOrgUnit | undefined>;
|
updateAdOrgUnit(id: number, ou: Partial<AdOrgUnit>): Promise<AdOrgUnit | undefined>;
|
||||||
|
updateAdOrgUnitByObjectGUID(connectionId: number, objectGUID: string, ou: Partial<AdOrgUnit>): Promise<AdOrgUnit | undefined>;
|
||||||
deleteAdOrgUnit(id: number): Promise<boolean>;
|
deleteAdOrgUnit(id: number): Promise<boolean>;
|
||||||
listAdOrgUnits(connectionId: number, query?: any): Promise<AdOrgUnit[]>;
|
listAdOrgUnits(connectionId: number, query?: any): Promise<AdOrgUnit[]>;
|
||||||
|
|
||||||
// AD Computers
|
// AD Computers
|
||||||
getAdComputer(id: number): Promise<AdComputer | undefined>;
|
getAdComputer(id: number): Promise<AdComputer | undefined>;
|
||||||
|
getAdComputerByObjectGUID(connectionId: number, objectGUID: string): Promise<AdComputer | undefined>;
|
||||||
createAdComputer(computer: InsertAdComputer): Promise<AdComputer>;
|
createAdComputer(computer: InsertAdComputer): Promise<AdComputer>;
|
||||||
updateAdComputer(id: number, computer: Partial<AdComputer>): Promise<AdComputer | undefined>;
|
updateAdComputer(id: number, computer: Partial<AdComputer>): Promise<AdComputer | undefined>;
|
||||||
|
updateAdComputerByObjectGUID(connectionId: number, objectGUID: string, computer: Partial<AdComputer>): Promise<AdComputer | undefined>;
|
||||||
deleteAdComputer(id: number): Promise<boolean>;
|
deleteAdComputer(id: number): Promise<boolean>;
|
||||||
listAdComputers(connectionId: number, query?: any): Promise<AdComputer[]>;
|
listAdComputers(connectionId: number, query?: any): Promise<AdComputer[]>;
|
||||||
|
|
||||||
@@ -559,6 +567,18 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAdUserByObjectGUID(connectionId: number, objectGUID: string): Promise<AdUser | undefined> {
|
||||||
|
const result = await db.select()
|
||||||
|
.from(adUsers)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adUsers.connectionId, connectionId),
|
||||||
|
eq(adUsers.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async createAdUser(user: InsertAdUser): Promise<AdUser> {
|
async createAdUser(user: InsertAdUser): Promise<AdUser> {
|
||||||
const result = await db.insert(adUsers).values(user).returning();
|
const result = await db.insert(adUsers).values(user).returning();
|
||||||
return result[0];
|
return result[0];
|
||||||
@@ -569,6 +589,19 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateAdUserByObjectGUID(connectionId: number, objectGUID: string, userData: Partial<AdUser>): Promise<AdUser | undefined> {
|
||||||
|
const result = await db.update(adUsers)
|
||||||
|
.set(userData)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adUsers.connectionId, connectionId),
|
||||||
|
eq(adUsers.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning();
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async deleteAdUser(id: number): Promise<boolean> {
|
async deleteAdUser(id: number): Promise<boolean> {
|
||||||
const result = await db.delete(adUsers).where(eq(adUsers.id, id)).returning({ id: adUsers.id });
|
const result = await db.delete(adUsers).where(eq(adUsers.id, id)).returning({ id: adUsers.id });
|
||||||
return result.length > 0;
|
return result.length > 0;
|
||||||
@@ -648,6 +681,18 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAdGroupByObjectGUID(connectionId: number, objectGUID: string): Promise<AdGroup | undefined> {
|
||||||
|
const result = await db.select()
|
||||||
|
.from(adGroups)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adGroups.connectionId, connectionId),
|
||||||
|
eq(adGroups.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async createAdGroup(group: InsertAdGroup): Promise<AdGroup> {
|
async createAdGroup(group: InsertAdGroup): Promise<AdGroup> {
|
||||||
const result = await db.insert(adGroups).values(group).returning();
|
const result = await db.insert(adGroups).values(group).returning();
|
||||||
return result[0];
|
return result[0];
|
||||||
@@ -658,6 +703,19 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateAdGroupByObjectGUID(connectionId: number, objectGUID: string, groupData: Partial<AdGroup>): Promise<AdGroup | undefined> {
|
||||||
|
const result = await db.update(adGroups)
|
||||||
|
.set(groupData)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adGroups.connectionId, connectionId),
|
||||||
|
eq(adGroups.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning();
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async deleteAdGroup(id: number): Promise<boolean> {
|
async deleteAdGroup(id: number): Promise<boolean> {
|
||||||
const result = await db.delete(adGroups).where(eq(adGroups.id, id)).returning({ id: adGroups.id });
|
const result = await db.delete(adGroups).where(eq(adGroups.id, id)).returning({ id: adGroups.id });
|
||||||
return result.length > 0;
|
return result.length > 0;
|
||||||
@@ -737,6 +795,18 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAdOrgUnitByObjectGUID(connectionId: number, objectGUID: string): Promise<AdOrgUnit | undefined> {
|
||||||
|
const result = await db.select()
|
||||||
|
.from(adOrgUnits)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adOrgUnits.connectionId, connectionId),
|
||||||
|
eq(adOrgUnits.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async createAdOrgUnit(ou: InsertAdOrgUnit): Promise<AdOrgUnit> {
|
async createAdOrgUnit(ou: InsertAdOrgUnit): Promise<AdOrgUnit> {
|
||||||
const result = await db.insert(adOrgUnits).values(ou).returning();
|
const result = await db.insert(adOrgUnits).values(ou).returning();
|
||||||
return result[0];
|
return result[0];
|
||||||
@@ -747,6 +817,19 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateAdOrgUnitByObjectGUID(connectionId: number, objectGUID: string, ouData: Partial<AdOrgUnit>): Promise<AdOrgUnit | undefined> {
|
||||||
|
const result = await db.update(adOrgUnits)
|
||||||
|
.set(ouData)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adOrgUnits.connectionId, connectionId),
|
||||||
|
eq(adOrgUnits.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning();
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async deleteAdOrgUnit(id: number): Promise<boolean> {
|
async deleteAdOrgUnit(id: number): Promise<boolean> {
|
||||||
const result = await db.delete(adOrgUnits).where(eq(adOrgUnits.id, id)).returning({ id: adOrgUnits.id });
|
const result = await db.delete(adOrgUnits).where(eq(adOrgUnits.id, id)).returning({ id: adOrgUnits.id });
|
||||||
return result.length > 0;
|
return result.length > 0;
|
||||||
@@ -780,6 +863,18 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAdComputerByObjectGUID(connectionId: number, objectGUID: string): Promise<AdComputer | undefined> {
|
||||||
|
const result = await db.select()
|
||||||
|
.from(adComputers)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adComputers.connectionId, connectionId),
|
||||||
|
eq(adComputers.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async createAdComputer(computer: InsertAdComputer): Promise<AdComputer> {
|
async createAdComputer(computer: InsertAdComputer): Promise<AdComputer> {
|
||||||
const result = await db.insert(adComputers).values(computer).returning();
|
const result = await db.insert(adComputers).values(computer).returning();
|
||||||
return result[0];
|
return result[0];
|
||||||
@@ -790,6 +885,19 @@ export class DatabaseStorage implements IStorage {
|
|||||||
return result.length > 0 ? result[0] : undefined;
|
return result.length > 0 ? result[0] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateAdComputerByObjectGUID(connectionId: number, objectGUID: string, computerData: Partial<AdComputer>): Promise<AdComputer | undefined> {
|
||||||
|
const result = await db.update(adComputers)
|
||||||
|
.set(computerData)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(adComputers.connectionId, connectionId),
|
||||||
|
eq(adComputers.objectGUID, objectGUID)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning();
|
||||||
|
return result.length > 0 ? result[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async deleteAdComputer(id: number): Promise<boolean> {
|
async deleteAdComputer(id: number): Promise<boolean> {
|
||||||
const result = await db.delete(adComputers).where(eq(adComputers.id, id)).returning({ id: adComputers.id });
|
const result = await db.delete(adComputers).where(eq(adComputers.id, id)).returning({ id: adComputers.id });
|
||||||
return result.length > 0;
|
return result.length > 0;
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ export const adUsers = pgTable("ad_users", {
|
|||||||
email: text("email"),
|
email: text("email"),
|
||||||
enabled: boolean("enabled").default(true),
|
enabled: boolean("enabled").default(true),
|
||||||
lastLogon: timestamp("last_logon"),
|
lastLogon: timestamp("last_logon"),
|
||||||
|
managedBy: text("managed_by"),
|
||||||
memberOf: jsonb("member_of"),
|
memberOf: jsonb("member_of"),
|
||||||
adProperties: jsonb("ad_properties"),
|
adProperties: jsonb("ad_properties"),
|
||||||
});
|
});
|
||||||
@@ -174,6 +175,7 @@ export const adGroups = pgTable("ad_groups", {
|
|||||||
sAMAccountName: text("sam_account_name").notNull(),
|
sAMAccountName: text("sam_account_name").notNull(),
|
||||||
groupType: text("group_type"),
|
groupType: text("group_type"),
|
||||||
description: text("description"),
|
description: text("description"),
|
||||||
|
managedBy: text("managed_by"),
|
||||||
members: jsonb("members"),
|
members: jsonb("members"),
|
||||||
adProperties: jsonb("ad_properties"),
|
adProperties: jsonb("ad_properties"),
|
||||||
});
|
});
|
||||||
@@ -187,6 +189,7 @@ export const adOrgUnits = pgTable("ad_org_units", {
|
|||||||
cn: text("cn"),
|
cn: text("cn"),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
description: text("description"),
|
description: text("description"),
|
||||||
|
managedBy: text("managed_by"),
|
||||||
adProperties: jsonb("ad_properties"),
|
adProperties: jsonb("ad_properties"),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -204,6 +207,7 @@ export const adComputers = pgTable("ad_computers", {
|
|||||||
operatingSystemVersion: text("operating_system_version"),
|
operatingSystemVersion: text("operating_system_version"),
|
||||||
lastLogon: timestamp("last_logon"),
|
lastLogon: timestamp("last_logon"),
|
||||||
enabled: boolean("enabled").default(true),
|
enabled: boolean("enabled").default(true),
|
||||||
|
managedBy: text("managed_by"),
|
||||||
memberOf: jsonb("member_of"),
|
memberOf: jsonb("member_of"),
|
||||||
adProperties: jsonb("ad_properties"),
|
adProperties: jsonb("ad_properties"),
|
||||||
});
|
});
|
||||||
@@ -307,6 +311,13 @@ export const removeFromGroupSchema = z.object({
|
|||||||
objectType: z.enum(["user", "computer"]),
|
objectType: z.enum(["user", "computer"]),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ManagedBy operation schema
|
||||||
|
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;
|
||||||
export type InsertRole = z.infer<typeof insertRoleSchema>;
|
export type InsertRole = z.infer<typeof insertRoleSchema>;
|
||||||
@@ -339,6 +350,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