From 35df17334cd2577da48e0b1ea551b174ab76793a Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Mon, 19 Jan 2026 21:21:12 -0800 Subject: [PATCH] sdk: AccessControls --- sdk/package-lock.json | 4 +- sdk/package.json | 2 +- sdk/src/acl.ts | 100 +++++++++++++++++- sdk/types/package-lock.json | 4 +- sdk/types/package.json | 2 +- .../scrypted_python/scrypted_sdk/types.py | 2 +- 6 files changed, 106 insertions(+), 8 deletions(-) diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 955f56056..a7c40515d 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/sdk", - "version": "0.5.55", + "version": "0.5.57", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@scrypted/sdk", - "version": "0.5.55", + "version": "0.5.57", "license": "ISC", "dependencies": { "@babel/preset-typescript": "^7.27.1", diff --git a/sdk/package.json b/sdk/package.json index 4c911e200..3eaf4a7eb 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/sdk", - "version": "0.5.55", + "version": "0.5.57", "description": "", "main": "dist/src/index.js", "exports": { diff --git a/sdk/src/acl.ts b/sdk/src/acl.ts index b3f473a2d..492bd2720 100644 --- a/sdk/src/acl.ts +++ b/sdk/src/acl.ts @@ -1,4 +1,4 @@ -import { ScryptedDeviceAccessControl, ScryptedInterface, ScryptedInterfaceDescriptors, ScryptedUserAccessControl } from "."; +import { EventDetails, ScryptedDeviceAccessControl, ScryptedInterface, ScryptedInterfaceDescriptors, ScryptedUserAccessControl } from "."; export function addAccessControlsForInterface(id: string, ...scryptedInterfaces: ScryptedInterface[]): ScryptedDeviceAccessControl { const methods = scryptedInterfaces.map(scryptedInterface => ScryptedInterfaceDescriptors[scryptedInterface]?.methods || []).flat(); @@ -20,3 +20,101 @@ export function mergeDeviceAccessControls(accessControls: ScryptedUserAccessCont accessControls.devicesAccessControls.push(...dacls); return accessControls; } + +export class AccessControls { + constructor(public acl: ScryptedUserAccessControl) { + } + + deny(reason: string = 'User does not have permission') { + throw new Error(reason); + } + + shouldRejectDevice(id: string) { + if (this.acl.devicesAccessControls === null) + return false; + + if (!this.acl.devicesAccessControls) + return true; + + const dacls = this.acl.devicesAccessControls.filter(dacl => dacl.id === id); + return !dacls.length; + } + + shouldRejectProperty(id: string, property: string) { + if (this.acl.devicesAccessControls === null) + return false; + + if (!this.acl.devicesAccessControls) + return true; + + const dacls = this.acl.devicesAccessControls.filter(dacl => dacl.id === id); + + for (const dacl of dacls) { + if (!dacl.properties || dacl.properties.includes(property)) + return false; + } + + return true; + } + + shouldRejectEvent(id: string, eventDetails: EventDetails) { + if (this.acl.devicesAccessControls === null) + return false; + + if (!this.acl.devicesAccessControls) + return true; + + const dacls = this.acl.devicesAccessControls.filter(dacl => dacl.id === id); + + const { property } = eventDetails; + if (property) { + for (const dacl of dacls) { + if (!dacl.properties || dacl.properties.includes(property)) + return false; + } + } + + const { eventInterface } = eventDetails; + + for (const dacl of dacls) { + if (!dacl.interfaces || dacl.interfaces.includes(eventInterface!)) + return false; + } + + return true; + } + + shouldRejectInterface(id: string, scryptedInterface: ScryptedInterface) { + if (this.acl.devicesAccessControls === null) + return false; + + if (!this.acl.devicesAccessControls) + return true; + + const dacls = this.acl.devicesAccessControls.filter(dacl => dacl.id === id); + + for (const dacl of dacls) { + if (!dacl.interfaces || dacl.interfaces.includes(scryptedInterface)) + return false; + } + + return true; + } + + shouldRejectMethod(id: string, method: string) { + if (this.acl.devicesAccessControls === null) + return false; + + if (!this.acl.devicesAccessControls) + return true; + + const dacls = this.acl.devicesAccessControls.filter(dacl => dacl.id === id); + + for (const dacl of dacls) { + if (!dacl.methods || dacl.methods.includes(method)) + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/sdk/types/package-lock.json b/sdk/types/package-lock.json index d88382067..a4e613777 100644 --- a/sdk/types/package-lock.json +++ b/sdk/types/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/types", - "version": "0.5.52", + "version": "0.5.53", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/types", - "version": "0.5.52", + "version": "0.5.53", "license": "ISC", "dependencies": { "openai": "^6.1.0" diff --git a/sdk/types/package.json b/sdk/types/package.json index e74088cdd..37ea9ff80 100644 --- a/sdk/types/package.json +++ b/sdk/types/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/types", - "version": "0.5.52", + "version": "0.5.53", "description": "", "main": "dist/index.js", "author": "", diff --git a/sdk/types/scrypted_python/scrypted_sdk/types.py b/sdk/types/scrypted_python/scrypted_sdk/types.py index 44b28182e..1d7e958cc 100644 --- a/sdk/types/scrypted_python/scrypted_sdk/types.py +++ b/sdk/types/scrypted_python/scrypted_sdk/types.py @@ -1131,7 +1131,7 @@ class TamperState(TypedDict): pass -TYPES_VERSION = "0.5.52" +TYPES_VERSION = "0.5.53" class AirPurifier: