fix(orchestrator): encrypt OpenClaw provider tokens at rest
All checks were successful
ci/woodpecker/push/ci Pipeline was successful

This commit is contained in:
2026-03-07 16:55:51 -06:00
parent ff73fbd391
commit d60165572a
4 changed files with 148 additions and 7 deletions

View File

@@ -1,12 +1,19 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import type { AgentProviderConfig, Prisma } from "@prisma/client";
import { EncryptionService } from "../../security/encryption.service";
import { PrismaService } from "../../prisma/prisma.service";
import { CreateAgentProviderDto } from "./dto/create-agent-provider.dto";
import { UpdateAgentProviderDto } from "./dto/update-agent-provider.dto";
const OPENCLAW_PROVIDER_TYPE = "openclaw";
const OPENCLAW_TOKEN_KEYS = ["apiToken", "token", "bearerToken"] as const;
@Injectable()
export class AgentProvidersService {
constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
private readonly encryptionService: EncryptionService
) {}
async list(): Promise<AgentProviderConfig[]> {
return this.prisma.agentProviderConfig.findMany({
@@ -27,20 +34,23 @@ export class AgentProvidersService {
}
async create(dto: CreateAgentProviderDto): Promise<AgentProviderConfig> {
const credentials = this.sanitizeCredentials(dto.provider, dto.credentials ?? {});
return this.prisma.agentProviderConfig.create({
data: {
workspaceId: dto.workspaceId,
name: dto.name,
provider: dto.provider,
gatewayUrl: dto.gatewayUrl,
credentials: this.toJsonValue(dto.credentials ?? {}),
credentials: this.toJsonValue(credentials),
...(dto.isActive !== undefined ? { isActive: dto.isActive } : {}),
},
});
}
async update(id: string, dto: UpdateAgentProviderDto): Promise<AgentProviderConfig> {
await this.getById(id);
const existingConfig = await this.getById(id);
const provider = dto.provider ?? existingConfig.provider;
const data: Prisma.AgentProviderConfigUpdateInput = {
...(dto.workspaceId !== undefined ? { workspaceId: dto.workspaceId } : {}),
@@ -48,7 +58,9 @@ export class AgentProvidersService {
...(dto.provider !== undefined ? { provider: dto.provider } : {}),
...(dto.gatewayUrl !== undefined ? { gatewayUrl: dto.gatewayUrl } : {}),
...(dto.isActive !== undefined ? { isActive: dto.isActive } : {}),
...(dto.credentials !== undefined ? { credentials: this.toJsonValue(dto.credentials) } : {}),
...(dto.credentials !== undefined
? { credentials: this.toJsonValue(this.sanitizeCredentials(provider, dto.credentials)) }
: {}),
};
return this.prisma.agentProviderConfig.update({
@@ -65,6 +77,25 @@ export class AgentProvidersService {
});
}
private sanitizeCredentials(
provider: string,
credentials: Record<string, unknown>
): Record<string, unknown> {
if (provider.toLowerCase() !== OPENCLAW_PROVIDER_TYPE) {
return credentials;
}
const nextCredentials: Record<string, unknown> = { ...credentials };
for (const key of OPENCLAW_TOKEN_KEYS) {
const tokenValue = nextCredentials[key];
if (typeof tokenValue === "string" && tokenValue.length > 0) {
nextCredentials[key] = this.encryptionService.encryptIfNeeded(tokenValue);
}
}
return nextCredentials;
}
private toJsonValue(value: Record<string, unknown>): Prisma.InputJsonValue {
return value as Prisma.InputJsonValue;
}