-
Notifications
You must be signed in to change notification settings - Fork 117
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extension: Add model/resource for configuration + poké plugin to blac…
…klist domains
- Loading branch information
Showing
10 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
front/lib/api/poke/plugins/workspaces/extension_blacklist_domains.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { Err, Ok } from "@dust-tt/types"; | ||
|
||
import { createPlugin } from "@app/lib/api/poke/types"; | ||
import { updateExtensionConfiguration } from "@app/lib/api/workspace"; | ||
|
||
export const extensionBlacklistDomains = createPlugin( | ||
{ | ||
id: "extension-blacklist-domains", | ||
name: "Extension Blacklist Domains", | ||
description: "Update the list of blacklisted domains for the extension", | ||
resourceTypes: ["workspaces"], | ||
args: { | ||
domains: { | ||
type: "string", | ||
label: "Blacklisted domains", | ||
description: | ||
"Comma-separated list of domains to blacklist for the extension.", | ||
}, | ||
}, | ||
}, | ||
async (auth, resourceId, args) => { | ||
// Split by comma and remove any empty strings, and check domains are valid | ||
const domains = args.domains | ||
? args.domains | ||
.split(",") | ||
.map((d) => d.trim()) | ||
.filter((d) => d) | ||
: []; | ||
|
||
if (!areDomainsValid(domains)) { | ||
return new Err( | ||
new Error( | ||
"One or more domains are invalid. Please check the domain format." | ||
) | ||
); | ||
} | ||
|
||
const res = await updateExtensionConfiguration(auth, domains); | ||
if (res.isErr()) { | ||
return res; | ||
} | ||
|
||
return new Ok({ | ||
display: "text", | ||
value: `Blacklisted domains updated.`, | ||
}); | ||
} | ||
); | ||
|
||
function areDomainsValid(domains: string[]): boolean { | ||
if (domains.length === 0) { | ||
return true; // Empty domains array is valid | ||
} | ||
|
||
// Regular expression for domain validation | ||
// - Starts with alphanumeric or hyphen | ||
// - Can contain alphanumeric, hyphens | ||
// - Must have at least one dot | ||
// - TLD must be at least 2 characters | ||
// - Cannot start or end with hyphen | ||
// - Cannot have consecutive hyphens | ||
const domainRegex = | ||
/^(?!-)[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}(?!-)$/; | ||
|
||
return domains.every((domain) => { | ||
if (domain.length > 253) { | ||
return false; | ||
} | ||
if (!domainRegex.test(domain)) { | ||
return false; | ||
} | ||
const labels = domain.split("."); | ||
if (labels.some((label) => label.length > 63)) { | ||
return false; | ||
} | ||
|
||
return true; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
export * from "./create_space"; | ||
export * from "./disable_sso_enforcement"; | ||
export * from "./extend_trial"; | ||
export * from "./extension_blacklist_domains"; | ||
export * from "./invite_user"; | ||
export * from "./rename_workspace"; | ||
export * from "./reset_message_rate_limit"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import type { CreationOptional, ForeignKey, NonAttribute } from "sequelize"; | ||
import { DataTypes } from "sequelize"; | ||
|
||
import { Workspace } from "@app/lib/models/workspace"; | ||
import { frontSequelize } from "@app/lib/resources/storage"; | ||
import { BaseModel } from "@app/lib/resources/storage/wrappers"; | ||
|
||
export class ExtensionConfigurationModel extends BaseModel<ExtensionConfigurationModel> { | ||
declare createdAt: CreationOptional<Date>; | ||
declare updatedAt: CreationOptional<Date>; | ||
|
||
declare blacklistedDomains: string[]; | ||
|
||
declare workspaceId: ForeignKey<Workspace["id"]>; | ||
declare workspace: NonAttribute<Workspace>; | ||
} | ||
ExtensionConfigurationModel.init( | ||
{ | ||
createdAt: { | ||
type: DataTypes.DATE, | ||
allowNull: false, | ||
defaultValue: DataTypes.NOW, | ||
}, | ||
updatedAt: { | ||
type: DataTypes.DATE, | ||
allowNull: false, | ||
defaultValue: DataTypes.NOW, | ||
}, | ||
blacklistedDomains: { | ||
type: DataTypes.ARRAY(DataTypes.STRING), | ||
allowNull: false, | ||
defaultValue: [], | ||
}, | ||
}, | ||
{ | ||
modelName: "extension_configuration", | ||
sequelize: frontSequelize, | ||
indexes: [{ unique: true, fields: ["workspaceId"] }], | ||
} | ||
); | ||
|
||
Workspace.hasOne(ExtensionConfigurationModel, { | ||
foreignKey: { allowNull: false }, | ||
onDelete: "RESTRICT", | ||
}); | ||
|
||
ExtensionConfigurationModel.belongsTo(Workspace, { | ||
foreignKey: { allowNull: false }, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import type { | ||
ExtensionConfigurationType, | ||
ModelId, | ||
Result, | ||
} from "@dust-tt/types"; | ||
import { Err, Ok } from "@dust-tt/types"; | ||
import type { | ||
Attributes, | ||
CreationAttributes, | ||
ModelStatic, | ||
Transaction, | ||
} from "sequelize"; | ||
|
||
import type { Authenticator } from "@app/lib/auth"; | ||
import { ExtensionConfigurationModel } from "@app/lib/models/extension"; | ||
import { BaseResource } from "@app/lib/resources/base_resource"; | ||
import type { ReadonlyAttributesType } from "@app/lib/resources/storage/types"; | ||
import { makeSId } from "@app/lib/resources/string_ids"; | ||
|
||
// Attributes are marked as read-only to reflect the stateless nature of our Resource. | ||
// This design will be moved up to BaseResource once we transition away from Sequelize. | ||
// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-unsafe-declaration-merging | ||
export interface ExtensionConfigurationResource | ||
extends ReadonlyAttributesType<ExtensionConfigurationModel> {} | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging | ||
export class ExtensionConfigurationResource extends BaseResource<ExtensionConfigurationModel> { | ||
static model: ModelStatic<ExtensionConfigurationModel> = | ||
ExtensionConfigurationModel; | ||
|
||
constructor( | ||
model: ModelStatic<ExtensionConfigurationModel>, | ||
blob: Attributes<ExtensionConfigurationModel> | ||
) { | ||
super(ExtensionConfigurationModel, blob); | ||
} | ||
|
||
get sId(): string { | ||
return ExtensionConfigurationResource.modelIdToSId({ | ||
id: this.id, | ||
workspaceId: this.workspaceId, | ||
}); | ||
} | ||
|
||
static modelIdToSId({ | ||
id, | ||
workspaceId, | ||
}: { | ||
id: ModelId; | ||
workspaceId: ModelId; | ||
}): string { | ||
return makeSId("extension", { | ||
id, | ||
workspaceId, | ||
}); | ||
} | ||
|
||
static async makeNew( | ||
blob: Omit<CreationAttributes<ExtensionConfigurationModel>, "workspaceId">, | ||
workspaceId: ModelId | ||
) { | ||
const config = await ExtensionConfigurationModel.create({ | ||
...blob, | ||
workspaceId, | ||
}); | ||
|
||
return new this(ExtensionConfigurationModel, config.get()); | ||
} | ||
|
||
async delete( | ||
auth: Authenticator, | ||
{ transaction }: { transaction?: Transaction } | ||
): Promise<Result<undefined, Error>> { | ||
try { | ||
await this.model.destroy({ | ||
where: { | ||
id: this.id, | ||
}, | ||
transaction, | ||
}); | ||
|
||
return new Ok(undefined); | ||
} catch (err) { | ||
return new Err(err as Error); | ||
} | ||
} | ||
|
||
static async fetchForWorkspace( | ||
auth: Authenticator | ||
): Promise<ExtensionConfigurationResource | null> { | ||
const workspaceId = auth.getNonNullableWorkspace().id; | ||
const config = await this.model.findOne({ | ||
where: { | ||
workspaceId, | ||
}, | ||
}); | ||
|
||
return config ? new this(ExtensionConfigurationModel, config.get()) : null; | ||
} | ||
|
||
async updateBlacklistedDomains( | ||
auth: Authenticator, | ||
{ | ||
blacklistedDomains, | ||
}: { | ||
blacklistedDomains: string[]; | ||
} | ||
) { | ||
if (this.workspaceId !== auth.getNonNullableWorkspace().id) { | ||
throw new Error( | ||
"Can't update extension configuration for another workspace." | ||
); | ||
} | ||
|
||
await this.update({ | ||
blacklistedDomains, | ||
}); | ||
} | ||
|
||
toJSON(): ExtensionConfigurationType { | ||
return { | ||
id: this.id, | ||
sId: this.sId, | ||
blacklistedDomains: this.blacklistedDomains, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
-- Migration created on Dec 30, 2024 | ||
CREATE TABLE IF NOT EXISTS "extension_configurations" ( | ||
"id" BIGSERIAL , | ||
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
"blacklistedDomains" VARCHAR(255)[] NOT NULL DEFAULT ARRAY[]::VARCHAR(255)[], | ||
"workspaceId" BIGINT NOT NULL REFERENCES "workspaces" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, | ||
PRIMARY KEY ("id") | ||
); | ||
CREATE UNIQUE INDEX "extension_configurations_workspace_id" ON "extension_configurations" ("workspaceId"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ModelId } from "../shared/model_id"; | ||
|
||
export type ExtensionConfigurationType = { | ||
id: ModelId; | ||
sId: string; | ||
blacklistedDomains: string[]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters