a lot of things

This commit is contained in:
2026-02-20 11:46:30 -06:00
parent 987a1c8a6a
commit 70284bc14e
37 changed files with 1080 additions and 79 deletions
+449 -11
View File
@@ -199,6 +199,273 @@ Check if the currently authenticated user has specific permissions. Accepts an a
---
## Other User Routes
### Get All Users
**GET** `/user/users`
Fetch a list of all users.
**Authentication Required:** Yes
**Required Permissions:** `user.read.other`, `user.list.other`
**Response:**
```json
{
"status": 200,
"message": "Users Fetched Successfully!",
"data": [
{
"id": "ckx...",
"name": "John Doe",
"email": "john.doe@example.com",
"login": "john.doe",
"image": "https://...",
"roles": ["admin"]
}
],
"successful": true
}
```
---
### Get User by ID
**GET** `/user/users/:identifier`
Fetch a specific user by their ID.
**Authentication Required:** Yes
**Required Permissions:** `user.read.other`
**Path Parameters:**
- `identifier` - The user's ID
**Response:**
```json
{
"status": 200,
"message": "User Fetched Successfully!",
"data": {
"id": "ckx...",
"name": "John Doe",
"email": "john.doe@example.com",
"login": "john.doe",
"image": "https://...",
"roles": ["admin"]
},
"successful": true
}
```
**Error Response (404):**
```json
{
"status": 404,
"message": "User with identifier 'ckx...' was not found.",
"error": "UserNotFound",
"successful": false
}
```
---
### Update User by ID
**PATCH** `/user/users/:identifier`
Update a specific user's information. Supports updating profile fields, roles, and direct permissions.
**Authentication Required:** Yes
**Required Permissions:** `user.write.other`
**Conditional Permissions:**
- If `roles` is included in the body: `user.roles.other` is also required
- If `permissions` is included in the body: `user.permissions.other` is also required
**Path Parameters:**
- `identifier` - The user's ID
**Request Body:**
All fields are optional. Include only the fields you want to update.
```json
{
"name": "Jane Doe",
"image": "https://example.com/avatar.jpg",
"roles": ["admin", "moderator"],
"permissions": ["credential.fetch", "company.fetch"]
}
```
| Field | Type | Description |
| ------------- | ---------- | -------------------------------------------------------------- |
| `name` | `string` | The user's display name |
| `image` | `string` | URL to the user's avatar image |
| `roles` | `string[]` | Array of role ids or monikers to assign (replaces all roles) |
| `permissions` | `string[]` | Array of permission nodes to assign (replaces all permissions) |
**Response:**
```json
{
"status": 200,
"message": "User Updated Successfully!",
"data": {
"id": "ckx...",
"name": "Jane Doe",
"email": "jane.doe@example.com",
"login": "jane.doe",
"image": "https://example.com/avatar.jpg",
"roles": ["admin", "moderator"]
},
"successful": true
}
```
**Error Response (403 - Missing role permission):**
```json
{
"status": 403,
"message": "You do not have permission to modify roles on another user.",
"error": "InsufficientPermission",
"successful": false
}
```
---
### Delete User by ID
**DELETE** `/user/users/:identifier`
Delete a specific user.
**Authentication Required:** Yes
**Required Permissions:** `user.delete.other`
**Path Parameters:**
- `identifier` - The user's ID
**Response:**
```json
{
"status": 200,
"message": "User Deleted Successfully!",
"data": {
"id": "ckx...",
"name": "John Doe",
"email": "john.doe@example.com",
"login": "john.doe",
"image": "https://...",
"roles": ["admin"]
},
"successful": true
}
```
---
### Get User Roles
**GET** `/user/users/:identifier/roles`
Fetch all roles assigned to a specific user.
**Authentication Required:** Yes
**Required Permissions:** `user.read.other`, `role.read`
**Path Parameters:**
- `identifier` - The user's ID
**Response:**
```json
{
"status": 200,
"message": "User Roles Fetched Successfully!",
"data": [
{
"id": "uuid...",
"title": "Administrator",
"moniker": "admin",
"permissions": ["*"]
}
],
"successful": true
}
```
---
### Check User Permissions (Other User)
**POST** `/user/users/:identifier/check-permission`
Check if a specific user has certain permissions.
**Authentication Required:** Yes
**Required Permissions:** `user.read.other`
**Path Parameters:**
- `identifier` - The user's ID
**Request Body:**
```json
{
"permissions": ["user.read", "company.fetch", "credential.write"]
}
```
**Response:**
```json
{
"status": 200,
"message": "Permission check completed.",
"data": {
"results": [
{
"permission": "user.read",
"hasPermission": true
},
{
"permission": "company.fetch",
"hasPermission": false
},
{
"permission": "credential.write",
"hasPermission": true
}
]
},
"successful": true
}
```
---
## Company Routes
### Get All Companies
@@ -342,6 +609,34 @@ Fetch configurations for a specific company from ConnectWise.
## Credential Routes
### Get Value Types
**GET** `/credential/valuetypes`
Returns all available field value types for credential type fields.
**Authentication Required:** Yes
**Response:**
```json
{
"status": 200,
"message": "Value Types Fetched Successfully!",
"data": [
"plain_text",
"license_key",
"ip_address",
"generic_secret",
"bitlocker_key",
"password"
],
"successful": true
}
```
---
### Get Credential by ID
**GET** `/credential/credentials/:id`
@@ -365,11 +660,27 @@ Fetch a single credential by its ID.
"data": {
"id": "ckx...",
"name": "AWS Credentials",
"notes": null,
"typeId": "cky...",
"companyId": "ckz...",
"fields": {
"accountId": "123456789"
},
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {
"id": "cky...",
"name": "AWS",
@@ -413,9 +724,27 @@ Fetch all credentials associated with a specific company.
{
"id": "ckx...",
"name": "AWS Credentials",
"notes": null,
"typeId": "cky...",
"companyId": "ckz...",
"fields": {...},
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {...},
"company": {...}
}
@@ -441,6 +770,7 @@ Create a new credential with validated and encrypted fields.
```json
{
"name": "Production AWS Credentials",
"notes": "Used for production S3 access",
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
@@ -469,7 +799,26 @@ Create a new credential with validated and encrypted fields.
"name": "Production AWS Credentials",
"typeId": "cky...",
"companyId": "ckz...",
"fields": {...}
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {...},
"company": {...}
},
"successful": true
}
@@ -481,7 +830,7 @@ Create a new credential with validated and encrypted fields.
**PATCH** `/credential/credentials/:id`
Update a credential's basic properties (name).
Update a credential's basic properties (name, notes) and/or field values. Secure fields are automatically encrypted.
**Authentication Required:** Yes
@@ -493,9 +842,22 @@ Update a credential's basic properties (name).
**Request Body:**
All properties are optional. Include only the properties you want to update.
```json
{
"name": "Updated Credential Name"
"name": "Updated Credential Name",
"notes": "Updated notes for this credential",
"fields": [
{
"fieldId": "accessKeyId",
"value": "AKIAIOSFODNN7EXAMPLE"
},
{
"fieldId": "secretAccessKey",
"value": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
]
}
```
@@ -508,7 +870,21 @@ Update a credential's basic properties (name).
"data": {
"id": "ckx...",
"name": "Updated Credential Name",
...
"notes": "Updated notes for this credential",
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7EXAMPLE"
}
],
"type": {...},
"company": {...}
},
"successful": true
}
@@ -536,12 +912,10 @@ Validate and update credential field values. Secure fields are automatically enc
{
"fields": [
{
"id": "ckx1...",
"fieldId": "accessKeyId",
"value": "AKIAIOSFODNN7NEWVALUE"
},
{
"id": "ckx2...",
"fieldId": "secretAccessKey",
"value": "newSecretKeyValue123"
}
@@ -558,7 +932,29 @@ Validate and update credential field values. Secure fields are automatically enc
"data": {
"id": "ckx...",
"name": "Production AWS Credentials",
"fields": {...}
"notes": null,
"typeId": "cky...",
"companyId": "ckz...",
"fields": [
{
"id": "accessKeyId",
"name": "Access Key ID",
"secure": false,
"required": true,
"valueType": "plain_text",
"value": "AKIAIOSFODNN7NEWVALUE"
},
{
"id": "secretAccessKey",
"name": "Secret Access Key",
"secure": true,
"required": true,
"valueType": "password",
"value": null
}
],
"type": {...},
"company": {...}
},
"successful": true
}
@@ -634,6 +1030,48 @@ Decrypt and return all secure field values for a credential.
---
### Read Single Secure Value
**GET** `/credential/credentials/:id/secure-values/:fieldId`
Decrypt and return a single secure field value for a credential.
**Authentication Required:** Yes
**Required Permissions:** `credential.fetch`, `credential.secure_values.read`
**URL Parameters:**
- `id` - Credential ID
- `fieldId` - The field ID of the secure value to read
**Response:**
```json
{
"status": 200,
"message": "Secure Value Fetched Successfully!",
"data": {
"fieldId": "secretAccessKey",
"value": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
},
"successful": true
}
```
**Error Response (404):**
```json
{
"status": 404,
"message": "Secure field not found: unknownField",
"error": "SecureFieldNotFound",
"successful": false
}
```
---
### Delete Credential
**DELETE** `/credential/credentials/:id`
+20 -14
View File
@@ -32,16 +32,16 @@ The permission validator supports special tokens for flexible permission managem
### Credential Permissions
| Permission Node | Description | Used In |
| ------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| `credential.create` | Create a new credential | [src/api/credentials/create.ts](src/api/credentials/create.ts) |
| `credential.fetch` | Fetch a single credential | [src/api/credentials/fetch.ts](src/api/credentials/fetch.ts) |
| `credential.fetch.many` | Fetch multiple credentials | [src/api/credentials/fetchByCompany.ts](src/api/credentials/fetchByCompany.ts) |
| `credential.update` | Update a credential | [src/api/credentials/update.ts](src/api/credentials/update.ts) |
| `credential.delete` | Delete a credential | [src/api/credentials/delete.ts](src/api/credentials/delete.ts) |
| `credential.fields.fetch` | Fetch credential fields (requires `credential.fetch` as well) | [src/api/credentials/fetchFields.ts](src/api/credentials/fetchFields.ts) |
| `credential.fields.update` | Update credential fields (requires `credential.update` as well) | [src/api/credentials/updateFields.ts](src/api/credentials/updateFields.ts) |
| `credential.secure_values.read` | Read secure values of a credential (requires `credential.fetch` as well) | [src/api/credentials/readSecureValues.ts](src/api/credentials/readSecureValues.ts) |
| Permission Node | Description | Used In |
| ------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `credential.create` | Create a new credential | [src/api/credentials/create.ts](src/api/credentials/create.ts) |
| `credential.fetch` | Fetch a single credential | [src/api/credentials/fetch.ts](src/api/credentials/fetch.ts) |
| `credential.fetch.many` | Fetch multiple credentials | [src/api/credentials/fetchByCompany.ts](src/api/credentials/fetchByCompany.ts) |
| `credential.update` | Update a credential | [src/api/credentials/update.ts](src/api/credentials/update.ts) |
| `credential.delete` | Delete a credential | [src/api/credentials/delete.ts](src/api/credentials/delete.ts) |
| `credential.fields.fetch` | Fetch credential fields (requires `credential.fetch` as well) | [src/api/credentials/fetchFields.ts](src/api/credentials/fetchFields.ts) |
| `credential.fields.update` | Update credential fields (requires `credential.update` as well) | [src/api/credentials/updateFields.ts](src/api/credentials/updateFields.ts) |
| `credential.secure_values.read` | Read secure values of a credential (requires `credential.fetch` as well) | [src/api/credentials/readSecureValues.ts](src/api/credentials/readSecureValues.ts), [src/api/credentials/readSecureValue.ts](src/api/credentials/readSecureValue.ts) |
### Credential Type Permissions
@@ -66,10 +66,16 @@ The permission validator supports special tokens for flexible permission managem
### User Permissions
| Permission Node | Description | Used In |
| --------------- | ----------------------- | -------------------------------------------------------- |
| `user.read` | Read user information | [src/api/user/@me/fetch.ts](src/api/user/@me/fetch.ts) |
| `user.write` | Update user information | [src/api/user/@me/update.ts](src/api/user/@me/update.ts) |
| Permission Node | Description | Used In | Dependencies |
| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `user.read` | Read user information | [src/api/user/@me/fetch.ts](src/api/user/@me/fetch.ts) | |
| `user.write` | Update user information | [src/api/user/@me/update.ts](src/api/user/@me/update.ts) | |
| `user.read.other` | Read other users' information | [src/api/user/fetch.ts](src/api/user/fetch.ts), [src/api/user/fetchRoles.ts](src/api/user/fetchRoles.ts), [src/api/user/checkPermission.ts](src/api/user/checkPermission.ts) | |
| `user.list.other` | List all users | [src/api/user/fetchAll.ts](src/api/user/fetchAll.ts) | `user.read.other` |
| `user.write.other` | Update other users' information | [src/api/user/update.ts](src/api/user/update.ts) | |
| `user.roles.other` | Modify roles assigned to other users | [src/api/user/update.ts](src/api/user/update.ts) | `user.write.other` |
| `user.permissions.other` | Modify direct permissions assigned to other users | [src/api/user/update.ts](src/api/user/update.ts) | `user.write.other` |
| `user.delete.other` | Delete other users | [src/api/user/delete.ts](src/api/user/delete.ts) | |
### Permission Routes
+2 -2
View File
@@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = {
"clientVersion": "7.3.0",
"engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735",
"activeProvider": "postgresql",
"inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel Session {\n id String @id @default(uuid())\n sessionKey String @unique @default(cuid())\n userId String\n expires DateTime\n refreshTokenGenerated Boolean @default(false)\n refreshedAt DateTime?\n invalidatedAt DateTime?\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel User {\n id String @id @default(cuid())\n roles Role[]\n permissions String?\n login String @unique\n name String?\n email String @unique\n emailVerified DateTime?\n image String?\n\n userId String @unique\n token String?\n\n sessions Session[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Role {\n id String @id @default(uuid())\n title String\n moniker String @unique // e.g. admin, super_admin, moderator\n\n permissions String\n users User[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Company {\n id String @id @default(cuid())\n name String\n\n cw_CompanyId Int @unique\n cw_Identifier String @unique\n\n credentials Credential[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CredentialType {\n id String @id @default(cuid())\n name String @unique\n\n permissionScope String\n icon String?\n fields Json\n\n credentials Credential[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel SecureValue {\n id String @id @default(cuid())\n name String\n\n content String // Encrypted content\n hash String // Hash of the original content for integrity verification and Search\n\n credentialId String\n credential Credential @relation(fields: [credentialId], references: [id], onDelete: Cascade)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Credential {\n id String @id @default(cuid())\n name String\n\n typeId String\n type CredentialType @relation(fields: [typeId], references: [id], onDelete: Cascade)\n\n fields Json\n\n companyId String\n company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)\n\n securevalues SecureValue[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n",
"inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel Session {\n id String @id @default(uuid())\n sessionKey String @unique @default(cuid())\n userId String\n expires DateTime\n refreshTokenGenerated Boolean @default(false)\n refreshedAt DateTime?\n invalidatedAt DateTime?\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel User {\n id String @id @default(cuid())\n roles Role[]\n permissions String?\n login String @unique\n name String?\n email String @unique\n emailVerified DateTime?\n image String?\n\n userId String @unique\n token String?\n\n sessions Session[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Role {\n id String @id @default(uuid())\n title String\n moniker String @unique // e.g. admin, super_admin, moderator\n\n permissions String\n users User[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Company {\n id String @id @default(cuid())\n name String\n\n cw_CompanyId Int @unique\n cw_Identifier String @unique\n\n credentials Credential[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel CredentialType {\n id String @id @default(cuid())\n name String @unique\n\n permissionScope String\n icon String?\n fields Json\n\n credentials Credential[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel SecureValue {\n id String @id @default(cuid())\n name String\n\n content String // Encrypted content\n hash String // Hash of the original content for integrity verification and Search\n\n credentialId String\n credential Credential @relation(fields: [credentialId], references: [id], onDelete: Cascade)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n\nmodel Credential {\n id String @id @default(cuid())\n name String\n notes String?\n\n typeId String\n type CredentialType @relation(fields: [typeId], references: [id], onDelete: Cascade)\n\n fields Json\n\n companyId String\n company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)\n\n securevalues SecureValue[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n",
"runtimeDataModel": {
"models": {},
"enums": {},
@@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = {
}
}
config.runtimeDataModel = JSON.parse("{\"models\":{\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessionKey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenGenerated\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"invalidatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"roles\",\"kind\":\"object\",\"type\":\"Role\",\"relationName\":\"RoleToUser\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"login\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Role\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moniker\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"RoleToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Company\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cw_CompanyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cw_Identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CredentialType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissionScope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"icon\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"SecureValue\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credentialId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credential\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Credential\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"typeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"object\",\"type\":\"CredentialType\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"securevalues\",\"kind\":\"object\",\"type\":\"SecureValue\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
config.runtimeDataModel = JSON.parse("{\"models\":{\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessionKey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshTokenGenerated\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"invalidatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"roles\",\"kind\":\"object\",\"type\":\"Role\",\"relationName\":\"RoleToUser\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"login\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"emailVerified\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"image\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sessions\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Role\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moniker\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissions\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"users\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"RoleToUser\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Company\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"cw_CompanyId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"cw_Identifier\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"CredentialType\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"permissionScope\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"icon\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"SecureValue\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"content\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credentialId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"credential\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Credential\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"typeId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"object\",\"type\":\"CredentialType\",\"relationName\":\"CredentialToCredentialType\"},{\"name\":\"fields\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"companyId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"company\",\"kind\":\"object\",\"type\":\"Company\",\"relationName\":\"CompanyToCredential\"},{\"name\":\"securevalues\",\"kind\":\"object\",\"type\":\"SecureValue\",\"relationName\":\"CredentialToSecureValue\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
const { Buffer } = await import('node:buffer')
@@ -1050,6 +1050,7 @@ export type SecureValueScalarFieldEnum = (typeof SecureValueScalarFieldEnum)[key
export const CredentialScalarFieldEnum = {
id: 'id',
name: 'name',
notes: 'notes',
typeId: 'typeId',
fields: 'fields',
companyId: 'companyId',
@@ -159,6 +159,7 @@ export type SecureValueScalarFieldEnum = (typeof SecureValueScalarFieldEnum)[key
export const CredentialScalarFieldEnum = {
id: 'id',
name: 'name',
notes: 'notes',
typeId: 'typeId',
fields: 'fields',
companyId: 'companyId',
+46 -1
View File
@@ -27,6 +27,7 @@ export type AggregateCredential = {
export type CredentialMinAggregateOutputType = {
id: string | null
name: string | null
notes: string | null
typeId: string | null
companyId: string | null
createdAt: Date | null
@@ -36,6 +37,7 @@ export type CredentialMinAggregateOutputType = {
export type CredentialMaxAggregateOutputType = {
id: string | null
name: string | null
notes: string | null
typeId: string | null
companyId: string | null
createdAt: Date | null
@@ -45,6 +47,7 @@ export type CredentialMaxAggregateOutputType = {
export type CredentialCountAggregateOutputType = {
id: number
name: number
notes: number
typeId: number
fields: number
companyId: number
@@ -57,6 +60,7 @@ export type CredentialCountAggregateOutputType = {
export type CredentialMinAggregateInputType = {
id?: true
name?: true
notes?: true
typeId?: true
companyId?: true
createdAt?: true
@@ -66,6 +70,7 @@ export type CredentialMinAggregateInputType = {
export type CredentialMaxAggregateInputType = {
id?: true
name?: true
notes?: true
typeId?: true
companyId?: true
createdAt?: true
@@ -75,6 +80,7 @@ export type CredentialMaxAggregateInputType = {
export type CredentialCountAggregateInputType = {
id?: true
name?: true
notes?: true
typeId?: true
fields?: true
companyId?: true
@@ -158,6 +164,7 @@ export type CredentialGroupByArgs<ExtArgs extends runtime.Types.Extensions.Inter
export type CredentialGroupByOutputType = {
id: string
name: string
notes: string | null
typeId: string
fields: runtime.JsonValue
companyId: string
@@ -189,6 +196,7 @@ export type CredentialWhereInput = {
NOT?: Prisma.CredentialWhereInput | Prisma.CredentialWhereInput[]
id?: Prisma.StringFilter<"Credential"> | string
name?: Prisma.StringFilter<"Credential"> | string
notes?: Prisma.StringNullableFilter<"Credential"> | string | null
typeId?: Prisma.StringFilter<"Credential"> | string
fields?: Prisma.JsonFilter<"Credential">
companyId?: Prisma.StringFilter<"Credential"> | string
@@ -202,6 +210,7 @@ export type CredentialWhereInput = {
export type CredentialOrderByWithRelationInput = {
id?: Prisma.SortOrder
name?: Prisma.SortOrder
notes?: Prisma.SortOrderInput | Prisma.SortOrder
typeId?: Prisma.SortOrder
fields?: Prisma.SortOrder
companyId?: Prisma.SortOrder
@@ -218,6 +227,7 @@ export type CredentialWhereUniqueInput = Prisma.AtLeast<{
OR?: Prisma.CredentialWhereInput[]
NOT?: Prisma.CredentialWhereInput | Prisma.CredentialWhereInput[]
name?: Prisma.StringFilter<"Credential"> | string
notes?: Prisma.StringNullableFilter<"Credential"> | string | null
typeId?: Prisma.StringFilter<"Credential"> | string
fields?: Prisma.JsonFilter<"Credential">
companyId?: Prisma.StringFilter<"Credential"> | string
@@ -231,6 +241,7 @@ export type CredentialWhereUniqueInput = Prisma.AtLeast<{
export type CredentialOrderByWithAggregationInput = {
id?: Prisma.SortOrder
name?: Prisma.SortOrder
notes?: Prisma.SortOrderInput | Prisma.SortOrder
typeId?: Prisma.SortOrder
fields?: Prisma.SortOrder
companyId?: Prisma.SortOrder
@@ -247,6 +258,7 @@ export type CredentialScalarWhereWithAggregatesInput = {
NOT?: Prisma.CredentialScalarWhereWithAggregatesInput | Prisma.CredentialScalarWhereWithAggregatesInput[]
id?: Prisma.StringWithAggregatesFilter<"Credential"> | string
name?: Prisma.StringWithAggregatesFilter<"Credential"> | string
notes?: Prisma.StringNullableWithAggregatesFilter<"Credential"> | string | null
typeId?: Prisma.StringWithAggregatesFilter<"Credential"> | string
fields?: Prisma.JsonWithAggregatesFilter<"Credential">
companyId?: Prisma.StringWithAggregatesFilter<"Credential"> | string
@@ -257,6 +269,7 @@ export type CredentialScalarWhereWithAggregatesInput = {
export type CredentialCreateInput = {
id?: string
name: string
notes?: string | null
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Date | string
updatedAt?: Date | string
@@ -268,6 +281,7 @@ export type CredentialCreateInput = {
export type CredentialUncheckedCreateInput = {
id?: string
name: string
notes?: string | null
typeId: string
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId: string
@@ -279,6 +293,7 @@ export type CredentialUncheckedCreateInput = {
export type CredentialUpdateInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -290,6 +305,7 @@ export type CredentialUpdateInput = {
export type CredentialUncheckedUpdateInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
typeId?: Prisma.StringFieldUpdateOperationsInput | string
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId?: Prisma.StringFieldUpdateOperationsInput | string
@@ -301,6 +317,7 @@ export type CredentialUncheckedUpdateInput = {
export type CredentialCreateManyInput = {
id?: string
name: string
notes?: string | null
typeId: string
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId: string
@@ -311,6 +328,7 @@ export type CredentialCreateManyInput = {
export type CredentialUpdateManyMutationInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -319,6 +337,7 @@ export type CredentialUpdateManyMutationInput = {
export type CredentialUncheckedUpdateManyInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
typeId?: Prisma.StringFieldUpdateOperationsInput | string
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId?: Prisma.StringFieldUpdateOperationsInput | string
@@ -344,6 +363,7 @@ export type CredentialScalarRelationFilter = {
export type CredentialCountOrderByAggregateInput = {
id?: Prisma.SortOrder
name?: Prisma.SortOrder
notes?: Prisma.SortOrder
typeId?: Prisma.SortOrder
fields?: Prisma.SortOrder
companyId?: Prisma.SortOrder
@@ -354,6 +374,7 @@ export type CredentialCountOrderByAggregateInput = {
export type CredentialMaxOrderByAggregateInput = {
id?: Prisma.SortOrder
name?: Prisma.SortOrder
notes?: Prisma.SortOrder
typeId?: Prisma.SortOrder
companyId?: Prisma.SortOrder
createdAt?: Prisma.SortOrder
@@ -363,6 +384,7 @@ export type CredentialMaxOrderByAggregateInput = {
export type CredentialMinOrderByAggregateInput = {
id?: Prisma.SortOrder
name?: Prisma.SortOrder
notes?: Prisma.SortOrder
typeId?: Prisma.SortOrder
companyId?: Prisma.SortOrder
createdAt?: Prisma.SortOrder
@@ -470,6 +492,7 @@ export type CredentialUpdateOneRequiredWithoutSecurevaluesNestedInput = {
export type CredentialCreateWithoutCompanyInput = {
id?: string
name: string
notes?: string | null
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Date | string
updatedAt?: Date | string
@@ -480,6 +503,7 @@ export type CredentialCreateWithoutCompanyInput = {
export type CredentialUncheckedCreateWithoutCompanyInput = {
id?: string
name: string
notes?: string | null
typeId: string
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Date | string
@@ -519,6 +543,7 @@ export type CredentialScalarWhereInput = {
NOT?: Prisma.CredentialScalarWhereInput | Prisma.CredentialScalarWhereInput[]
id?: Prisma.StringFilter<"Credential"> | string
name?: Prisma.StringFilter<"Credential"> | string
notes?: Prisma.StringNullableFilter<"Credential"> | string | null
typeId?: Prisma.StringFilter<"Credential"> | string
fields?: Prisma.JsonFilter<"Credential">
companyId?: Prisma.StringFilter<"Credential"> | string
@@ -529,6 +554,7 @@ export type CredentialScalarWhereInput = {
export type CredentialCreateWithoutTypeInput = {
id?: string
name: string
notes?: string | null
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Date | string
updatedAt?: Date | string
@@ -539,6 +565,7 @@ export type CredentialCreateWithoutTypeInput = {
export type CredentialUncheckedCreateWithoutTypeInput = {
id?: string
name: string
notes?: string | null
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId: string
createdAt?: Date | string
@@ -575,6 +602,7 @@ export type CredentialUpdateManyWithWhereWithoutTypeInput = {
export type CredentialCreateWithoutSecurevaluesInput = {
id?: string
name: string
notes?: string | null
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Date | string
updatedAt?: Date | string
@@ -585,6 +613,7 @@ export type CredentialCreateWithoutSecurevaluesInput = {
export type CredentialUncheckedCreateWithoutSecurevaluesInput = {
id?: string
name: string
notes?: string | null
typeId: string
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId: string
@@ -611,6 +640,7 @@ export type CredentialUpdateToOneWithWhereWithoutSecurevaluesInput = {
export type CredentialUpdateWithoutSecurevaluesInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -621,6 +651,7 @@ export type CredentialUpdateWithoutSecurevaluesInput = {
export type CredentialUncheckedUpdateWithoutSecurevaluesInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
typeId?: Prisma.StringFieldUpdateOperationsInput | string
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId?: Prisma.StringFieldUpdateOperationsInput | string
@@ -631,6 +662,7 @@ export type CredentialUncheckedUpdateWithoutSecurevaluesInput = {
export type CredentialCreateManyCompanyInput = {
id?: string
name: string
notes?: string | null
typeId: string
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Date | string
@@ -640,6 +672,7 @@ export type CredentialCreateManyCompanyInput = {
export type CredentialUpdateWithoutCompanyInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -650,6 +683,7 @@ export type CredentialUpdateWithoutCompanyInput = {
export type CredentialUncheckedUpdateWithoutCompanyInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
typeId?: Prisma.StringFieldUpdateOperationsInput | string
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -660,6 +694,7 @@ export type CredentialUncheckedUpdateWithoutCompanyInput = {
export type CredentialUncheckedUpdateManyWithoutCompanyInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
typeId?: Prisma.StringFieldUpdateOperationsInput | string
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -669,6 +704,7 @@ export type CredentialUncheckedUpdateManyWithoutCompanyInput = {
export type CredentialCreateManyTypeInput = {
id?: string
name: string
notes?: string | null
fields: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId: string
createdAt?: Date | string
@@ -678,6 +714,7 @@ export type CredentialCreateManyTypeInput = {
export type CredentialUpdateWithoutTypeInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -688,6 +725,7 @@ export type CredentialUpdateWithoutTypeInput = {
export type CredentialUncheckedUpdateWithoutTypeInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId?: Prisma.StringFieldUpdateOperationsInput | string
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -698,6 +736,7 @@ export type CredentialUncheckedUpdateWithoutTypeInput = {
export type CredentialUncheckedUpdateManyWithoutTypeInput = {
id?: Prisma.StringFieldUpdateOperationsInput | string
name?: Prisma.StringFieldUpdateOperationsInput | string
notes?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
fields?: Prisma.JsonNullValueInput | runtime.InputJsonValue
companyId?: Prisma.StringFieldUpdateOperationsInput | string
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
@@ -738,6 +777,7 @@ export type CredentialCountOutputTypeCountSecurevaluesArgs<ExtArgs extends runti
export type CredentialSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
id?: boolean
name?: boolean
notes?: boolean
typeId?: boolean
fields?: boolean
companyId?: boolean
@@ -752,6 +792,7 @@ export type CredentialSelect<ExtArgs extends runtime.Types.Extensions.InternalAr
export type CredentialSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
id?: boolean
name?: boolean
notes?: boolean
typeId?: boolean
fields?: boolean
companyId?: boolean
@@ -764,6 +805,7 @@ export type CredentialSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Ex
export type CredentialSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
id?: boolean
name?: boolean
notes?: boolean
typeId?: boolean
fields?: boolean
companyId?: boolean
@@ -776,6 +818,7 @@ export type CredentialSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Ex
export type CredentialSelectScalar = {
id?: boolean
name?: boolean
notes?: boolean
typeId?: boolean
fields?: boolean
companyId?: boolean
@@ -783,7 +826,7 @@ export type CredentialSelectScalar = {
updatedAt?: boolean
}
export type CredentialOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "typeId" | "fields" | "companyId" | "createdAt" | "updatedAt", ExtArgs["result"]["credential"]>
export type CredentialOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "notes" | "typeId" | "fields" | "companyId" | "createdAt" | "updatedAt", ExtArgs["result"]["credential"]>
export type CredentialInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
type?: boolean | Prisma.CredentialTypeDefaultArgs<ExtArgs>
company?: boolean | Prisma.CompanyDefaultArgs<ExtArgs>
@@ -809,6 +852,7 @@ export type $CredentialPayload<ExtArgs extends runtime.Types.Extensions.Internal
scalars: runtime.Types.Extensions.GetPayloadResult<{
id: string
name: string
notes: string | null
typeId: string
fields: runtime.JsonValue
companyId: string
@@ -1242,6 +1286,7 @@ export interface Prisma__CredentialClient<T, Null = never, ExtArgs extends runti
export interface CredentialFieldRefs {
readonly id: Prisma.FieldRef<"Credential", 'String'>
readonly name: Prisma.FieldRef<"Credential", 'String'>
readonly notes: Prisma.FieldRef<"Credential", 'String'>
readonly typeId: Prisma.FieldRef<"Credential", 'String'>
readonly fields: Prisma.FieldRef<"Credential", 'Json'>
readonly companyId: Prisma.FieldRef<"Credential", 'String'>
+3 -2
View File
@@ -97,8 +97,9 @@ model SecureValue {
}
model Credential {
id String @id @default(cuid())
name String
id String @id @default(cuid())
name String
notes String?
typeId String
type CredentialType @relation(fields: [typeId], references: [id], onDelete: Cascade)
+4 -1
View File
@@ -5,6 +5,7 @@ import { apiResponse } from "../../modules/api-utils/apiResponse";
import { ContentfulStatusCode } from "hono/utils/http-status";
import { authMiddleware } from "../middleware/authorization";
import { z } from "zod";
import { ValueType } from "../../modules/credentials/credentialTypeDefs";
/* /v1/credential-type */
export default createRoute(
@@ -24,13 +25,15 @@ export default createRoute(
name: z.string(),
required: z.boolean(),
secure: z.boolean(),
valueType: z.enum(["plain_text", "password"]),
valueType: z.enum(Object.values(ValueType)),
}),
),
});
const data = schema.parse(body);
console.log("Creating Credential Type with data:", data);
const credentialType = await credentialTypes.create(data as any);
const response = apiResponse.created(
+2 -1
View File
@@ -5,6 +5,7 @@ import { apiResponse } from "../../modules/api-utils/apiResponse";
import { ContentfulStatusCode } from "hono/utils/http-status";
import { authMiddleware } from "../middleware/authorization";
import { z } from "zod";
import { ValueType } from "../../modules/credentials/credentialTypeDefs";
/* /v1/credential-type/:id */
export default createRoute(
@@ -26,7 +27,7 @@ export default createRoute(
name: z.string(),
required: z.boolean(),
secure: z.boolean(),
valueType: z.enum(["plain_text", "password"]),
valueType: z.enum(Object.values(ValueType)),
}),
)
.optional(),
+1
View File
@@ -16,6 +16,7 @@ export default createRoute(
const schema = z.object({
name: z.string().min(1, "Name is required"),
notes: z.string().optional(),
typeId: z.string().min(1, "Type ID is required"),
companyId: z.string().min(1, "Company ID is required"),
fields: z.array(
+4
View File
@@ -5,9 +5,12 @@ import { default as update } from "./update";
import { default as updateFields } from "./updateFields";
import { default as fetchFields } from "./fetchFields";
import { default as readSecureValues } from "./readSecureValues";
import { default as readSecureValue } from "./readSecureValue";
import { default as deleteCredential } from "./delete";
import { default as valueTypes } from "./valueTypes";
export {
valueTypes,
fetch,
fetchByCompany,
create,
@@ -15,5 +18,6 @@ export {
updateFields,
fetchFields,
readSecureValues,
readSecureValue,
deleteCredential as delete,
};
+27
View File
@@ -0,0 +1,27 @@
import { Hono } from "hono/tiny";
import { createRoute } from "../../modules/api-utils/createRoute";
import { credentials } from "../../managers/credentials";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { ContentfulStatusCode } from "hono/utils/http-status";
import { authMiddleware } from "../middleware/authorization";
/* GET /v1/credential/credentials/:id/secure-values/:fieldId */
export default createRoute(
"get",
["/credentials/:id/secure-values/:fieldId"],
async (c) => {
const credential = await credentials.fetch(c.req.param("id"));
const fieldId = c.req.param("fieldId");
const value = await credential.readSecureFieldValue(fieldId);
const response = apiResponse.successful(
"Secure Value Fetched Successfully!",
{ fieldId, value },
);
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware({
permissions: ["credential.fetch", "credential.secure_values.read"],
}),
);
+19 -1
View File
@@ -17,11 +17,29 @@ export default createRoute(
const schema = z.object({
name: z.string().optional(),
notes: z.string().nullable().optional(),
fields: z
.array(
z.object({
fieldId: z.string(),
value: z.string(),
}),
)
.optional(),
});
const data = schema.parse(body);
await credential.update(data);
if (data.fields) {
await credential.validateAndUpdateFields(data.fields);
}
if (data.name !== undefined || data.notes !== undefined) {
await credential.update({
...(data.name !== undefined && { name: data.name }),
...(data.notes !== undefined && { notes: data.notes }),
});
}
const response = apiResponse.successful(
"Credential Updated Successfully!",
-1
View File
@@ -18,7 +18,6 @@ export default createRoute(
const schema = z.object({
fields: z.array(
z.object({
id: z.string(),
fieldId: z.string(),
value: z.string(),
}),
+22
View File
@@ -0,0 +1,22 @@
import { createRoute } from "../../modules/api-utils/createRoute";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { ContentfulStatusCode } from "hono/utils/http-status";
import { ValueType } from "../../modules/credentials/credentialTypeDefs";
import { authMiddleware } from "../middleware/authorization";
/* GET /v1/credential/valuetypes */
export default createRoute(
"get",
["/valuetypes"],
async (c) => {
const valueTypes = Object.values(ValueType);
const response = apiResponse.successful(
"Value Types Fetched Successfully!",
valueTypes,
);
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware(),
);
+3 -1
View File
@@ -1,7 +1,9 @@
import { Hono } from "hono";
import * as userRoutes from "../user/@me";
import * as meRoutes from "../user/@me";
import * as userRoutes from "../user";
const authRouter = new Hono();
Object.values(meRoutes).map((r) => authRouter.route("/", r));
Object.values(userRoutes).map((r) => authRouter.route("/", r));
export default authRouter;
+10 -1
View File
@@ -1,13 +1,22 @@
import { ContentfulStatusCode } from "hono/utils/http-status";
import { z } from "zod";
import { apiResponse } from "../../../modules/api-utils/apiResponse";
import { createRoute } from "../../../modules/api-utils/createRoute";
import { authMiddleware } from "../../middleware/authorization";
const updateSchema = z
.object({
name: z.string().optional(),
image: z.string().optional(),
})
.strict();
export default createRoute(
"patch",
["/@me"],
async (c) => {
const updatedUser = await c.get("user")?.update(await c.req.json());
const body = updateSchema.parse(await c.req.json());
const updatedUser = await c.get("user")?.update(body);
const response = apiResponse.successful(
"Successfully updated user.",
updatedUser?.toJson(),
+47
View File
@@ -0,0 +1,47 @@
import { ContentfulStatusCode } from "hono/utils/http-status";
import { z } from "zod";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { createRoute } from "../../modules/api-utils/createRoute";
import { authMiddleware } from "../middleware/authorization";
import { users } from "../../managers/users";
import GenericError from "../../Errors/GenericError";
const checkPermissionSchema = z.object({
permissions: z
.array(z.string().min(1, "Permission node cannot be empty"))
.min(1, "At least one permission is required"),
});
/* POST /v1/user/users/:identifier/check-permission */
export default createRoute(
"post",
["/users/:identifier/check-permission"],
async (c) => {
const identifier = c.req.param("identifier");
const user = await users.fetchUser({ id: identifier });
if (!user)
throw new GenericError({
name: "UserNotFound",
message: `User with identifier '${identifier}' was not found.`,
status: 404,
});
const body = await c.req.json();
const { permissions } = checkPermissionSchema.parse(body);
const results = await Promise.all(
permissions.map(async (permission) => ({
permission,
hasPermission: await user.hasPermission(permission),
})),
);
const response = apiResponse.successful("Permission check completed.", {
results,
});
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware({ permissions: ["user.read.other"] }),
);
+34
View File
@@ -0,0 +1,34 @@
import { ContentfulStatusCode } from "hono/utils/http-status";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { createRoute } from "../../modules/api-utils/createRoute";
import { authMiddleware } from "../middleware/authorization";
import { users } from "../../managers/users";
import GenericError from "../../Errors/GenericError";
/* DELETE /v1/user/users/:identifier */
export default createRoute(
"delete",
["/users/:identifier"],
async (c) => {
const identifier = c.req.param("identifier");
const user = await users.fetchUser({ id: identifier });
if (!user)
throw new GenericError({
name: "UserNotFound",
message: `User with identifier '${identifier}' was not found.`,
status: 404,
});
const userData = user.toJson();
await users.deleteUser(user.id);
const response = apiResponse.successful(
"User Deleted Successfully!",
userData,
);
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware({ permissions: ["user.delete.other"] }),
);
+31
View File
@@ -0,0 +1,31 @@
import { ContentfulStatusCode } from "hono/utils/http-status";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { createRoute } from "../../modules/api-utils/createRoute";
import { authMiddleware } from "../middleware/authorization";
import { users } from "../../managers/users";
import GenericError from "../../Errors/GenericError";
/* GET /v1/user/users/:identifier */
export default createRoute(
"get",
["/users/:identifier"],
async (c) => {
const identifier = c.req.param("identifier");
const user = await users.fetchUser({ id: identifier });
if (!user)
throw new GenericError({
name: "UserNotFound",
message: `User with identifier '${identifier}' was not found.`,
status: 404,
});
const response = apiResponse.successful(
"User Fetched Successfully!",
user.toJson(),
);
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware({ permissions: ["user.read.other"] }),
);
+23
View File
@@ -0,0 +1,23 @@
import { ContentfulStatusCode } from "hono/utils/http-status";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { createRoute } from "../../modules/api-utils/createRoute";
import { authMiddleware } from "../middleware/authorization";
import { users } from "../../managers/users";
/* GET /v1/user/users */
export default createRoute(
"get",
["/users"],
async (c) => {
const allUsers = await users.fetchAllUsers();
const usersArray = allUsers.map((u) => u.toJson());
const response = apiResponse.successful(
"Users Fetched Successfully!",
usersArray,
);
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware({ permissions: ["user.read.other", "user.list.other"] }),
);
+34
View File
@@ -0,0 +1,34 @@
import { ContentfulStatusCode } from "hono/utils/http-status";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { createRoute } from "../../modules/api-utils/createRoute";
import { authMiddleware } from "../middleware/authorization";
import { users } from "../../managers/users";
import GenericError from "../../Errors/GenericError";
/* GET /v1/user/users/:identifier/roles */
export default createRoute(
"get",
["/users/:identifier/roles"],
async (c) => {
const identifier = c.req.param("identifier");
const user = await users.fetchUser({ id: identifier });
if (!user)
throw new GenericError({
name: "UserNotFound",
message: `User with identifier '${identifier}' was not found.`,
status: 404,
});
const roles = await user.fetchRoles();
const rolesArray = roles.map((r) => r.toJson({ viewPermissions: true }));
const response = apiResponse.successful(
"User Roles Fetched Successfully!",
rolesArray,
);
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware({ permissions: ["user.read.other", "role.read"] }),
);
+6
View File
@@ -0,0 +1,6 @@
export { default as fetch } from "./fetch";
export { default as fetchAll } from "./fetchAll";
export { default as update } from "./update";
export { default as deleteUser } from "./delete";
export { default as fetchRoles } from "./fetchRoles";
export { default as checkPermission } from "./checkPermission";
+68
View File
@@ -0,0 +1,68 @@
import { ContentfulStatusCode } from "hono/utils/http-status";
import { z } from "zod";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { createRoute } from "../../modules/api-utils/createRoute";
import { authMiddleware } from "../middleware/authorization";
import { users } from "../../managers/users";
import GenericError from "../../Errors/GenericError";
const updateSchema = z
.object({
name: z.string().optional(),
image: z.string().optional(),
roles: z.array(z.string()).optional(),
permissions: z.array(z.string()).optional(),
})
.strict();
/* PATCH /v1/user/users/:identifier */
export default createRoute(
"patch",
["/users/:identifier"],
async (c) => {
const identifier = c.req.param("identifier");
const requestingUser = c.get("user");
const user = await users.fetchUser({ id: identifier });
if (!user)
throw new GenericError({
name: "UserNotFound",
message: `User with identifier '${identifier}' was not found.`,
status: 404,
});
const body = updateSchema.parse(await c.req.json());
if (body.roles && !(await requestingUser.hasPermission("user.roles.other")))
throw new GenericError({
name: "InsufficientPermission",
message: "You do not have permission to modify roles on another user.",
status: 403,
});
if (
body.permissions &&
!(await requestingUser.hasPermission("user.permissions.other"))
)
throw new GenericError({
name: "InsufficientPermission",
message:
"You do not have permission to modify permissions on another user.",
status: 403,
});
const { roles: roleIds, permissions, ...profileData } = body;
if (Object.keys(profileData).length > 0) await user.update(profileData);
if (roleIds) await user.setRoles(roleIds);
if (permissions) await user.setPermissions(permissions);
const response = apiResponse.successful(
"User Updated Successfully!",
user.toJson(),
);
return c.json(response, response.status as ContentfulStatusCode);
},
authMiddleware({ permissions: ["user.write.other"] }),
);
+3 -1
View File
@@ -80,7 +80,9 @@ export class CompanyController {
city: this.cw_Data?.city,
state: this.cw_Data?.state,
zip: this.cw_Data?.zip,
country: this.cw_Data?.country.name,
country: this.cw_Data?.country
? this.cw_Data.country.name
: "United States",
},
},
};
+17 -6
View File
@@ -10,6 +10,7 @@ import { fieldValidator } from "../modules/credentials/fieldValidator";
import {
CredentialField,
CredentialTypeField,
ValueType,
} from "../modules/credentials/credentialTypeDefs";
import { generateSecureValue } from "../modules/credentials/generateSecureValue";
import { readSecureValue } from "../modules/credentials/readSecureValue";
@@ -24,6 +25,7 @@ import GenericError from "../Errors/GenericError";
export class CredentialController {
public readonly id: string;
public name: string;
public notes: string | null;
public readonly typeId: string;
public readonly companyId: string;
public fields: any;
@@ -44,6 +46,7 @@ export class CredentialController {
) {
this.id = credentialData.id;
this.name = credentialData.name;
this.notes = credentialData.notes;
this.typeId = credentialData.typeId;
this.companyId = credentialData.companyId;
this._type = credentialData.type;
@@ -52,14 +55,21 @@ export class CredentialController {
this.fields = (() => {
let fields = credentialData.fields as Record<string, any>;
this._secureValues.forEach((sv) => (fields[sv.name] = `secure-${sv.id}`));
return (this._type.fields! as any).map((f: any) => ({
id: f.id,
name: f.name,
secure: f.secure,
required: f.required,
valueType: f.valueType as ValueType,
value: f.secure
? `secure-${this._secureValues.find((sv) => sv.name === f.id)?.id}`
: fields[f.id],
}));
return fields;
})();
this.createdAt = credentialData.createdAt;
this.updatedAt = credentialData.updatedAt;
console.log(credentialData);
}
/**
@@ -72,6 +82,7 @@ export class CredentialController {
*/
private _updateInternalValues(credentialData: Credential) {
this.name = credentialData.name;
this.notes = credentialData.notes;
this.fields = credentialData.fields;
this.updatedAt = credentialData.updatedAt;
}
@@ -174,7 +185,6 @@ export class CredentialController {
const nonSecureFields = this.fields as Record<string, any>;
Object.entries(nonSecureFields || {}).forEach(([fieldId, value]) => {
fields.push({
id: `${this.id}-${fieldId}`, // Generate a consistent ID
fieldId,
value: value as string,
});
@@ -183,7 +193,6 @@ export class CredentialController {
// Add secure fields from SecureValue table (encrypted)
this._secureValues.forEach((secureValue) => {
fields.push({
id: secureValue.id,
fieldId: secureValue.name,
value: secureValue.content, // Encrypted value
});
@@ -253,11 +262,12 @@ export class CredentialController {
* @returns {Promise<CredentialController>} - The updated credential controller
*/
async update(
data: Partial<Pick<Credential, "name">>,
data: Partial<Pick<Credential, "name" | "notes">>,
): Promise<CredentialController> {
const pData = z
.object({
name: z.string().optional(),
notes: z.string().nullable().optional(),
})
.strict()
.parse(data);
@@ -307,6 +317,7 @@ export class CredentialController {
return {
id: this.id,
name: this.name,
notes: this.notes,
typeId: this.typeId,
companyId: this.companyId,
fields: this.fields,
+92 -12
View File
@@ -1,5 +1,4 @@
import { Collection } from "@discordjs/collection";
import { z } from "zod";
import { Role } from "../../generated/prisma/client";
import { User } from "../../generated/prisma/browser";
import { SessionTokensObject } from "./SessionController";
@@ -9,6 +8,10 @@ import { prisma } from "../constants";
import { events } from "../modules/globalEvents";
import { RoleController } from "./RoleController";
import { roles } from "../managers/roles";
import { signPermissions } from "../modules/permission-utils/signPermissions";
import { DecodedPermissionsBlock } from "../types/PermissionTypes";
import jwt from "jsonwebtoken";
import { permissionsPrivateKey } from "../constants";
export default class UserController {
public id: string;
@@ -18,6 +21,7 @@ export default class UserController {
public image: string | null;
private _roles: Collection<string, Role>;
private _permissions: string | null;
public createdAt: Date;
public updatedAt: Date;
@@ -29,6 +33,7 @@ export default class UserController {
this.image = userdata.image;
this.updatedAt = userdata.updatedAt;
this.createdAt = userdata.createdAt;
this._permissions = userdata.permissions ?? null;
this._roles = (() => {
let collection = new Collection<string, Role>();
@@ -77,22 +82,13 @@ export default class UserController {
* @param data - A partial of the user data
* @returns {Promise<UserController>} - The updated user controller
*/
public async update(data: Partial<User>) {
// Parsed Data With Schema
const pData = z
.object({
name: z.string().optional(),
image: z.string().optional(),
})
.strict()
.parse(data);
public async update(data: Partial<Pick<User, "name" | "image">>) {
if (Object.keys(data).length == 0)
throw new BodyError("Body cannot be empty.");
const updatedUser = await prisma.user.update({
where: { id: this.id },
data: pData,
data,
});
this._updateInternalValues(updatedUser);
@@ -101,6 +97,87 @@ export default class UserController {
return this;
}
/**
* Set Roles
*
* Replace the user's roles with the provided array of role identifiers (id or moniker).
* Validates that each role exists before assigning.
*
* @param roleIdentifiers - Array of role ids or monikers to assign
* @returns {Promise<UserController>} - The updated user controller
*/
public async setRoles(roleIdentifiers: string[]): Promise<UserController> {
const resolvedRoles = await Promise.all(
roleIdentifiers.map((identifier) => roles.fetch(identifier)),
);
const updatedUser = await prisma.user.update({
where: { id: this.id },
data: {
roles: {
set: resolvedRoles.map((r) => ({ id: r.id })),
},
},
include: { roles: true },
});
this._updateInternalValues(updatedUser);
this._roles = new Collection<string, Role>();
updatedUser.roles.map((v: any) => this._roles.set(v.id, v));
for (const role of resolvedRoles) {
events.emit("user:role:assigned", { user: this, role });
}
return this;
}
/**
* Set Permissions
*
* Replace the user's direct permissions with the provided array of permission strings.
* Signs the permissions with the user issuer before storing.
*
* @param permissions - Array of permission node strings to assign
* @returns {Promise<UserController>} - The updated user controller
*/
public async setPermissions(permissions: string[]): Promise<UserController> {
const signed = signPermissions({
issuer: "user",
subject: this.id,
permissions,
});
const updatedUser = await prisma.user.update({
where: { id: this.id },
data: { permissions: signed },
});
this._updateInternalValues(updatedUser);
return this;
}
/**
* Read Permissions
*
* Verifies and decodes the user's direct permissions JWT and returns the array of
* permission node strings. Returns an empty array if the user has no direct permissions.
*
* @returns {string[]} The user's direct permission nodes
*/
public readPermissions(): string[] {
if (!this._permissions) return [];
const decoded = jwt.verify(this._permissions, permissionsPrivateKey, {
algorithms: ["RS256"],
issuer: "user",
subject: this.id,
}) as DecodedPermissionsBlock;
return decoded.permissions;
}
/**
* Fetch Roles
*
@@ -185,9 +262,12 @@ export default class UserController {
: this._roles.size > 0
? this._roles.map((v) => v.moniker)
: undefined,
permissions: opts?.safeReturn ? undefined : this.readPermissions(),
login: opts?.safeReturn ? undefined : this.login,
email: opts?.safeReturn ? undefined : this.email,
image: this.image,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
};
}
}
+4
View File
@@ -79,6 +79,8 @@ export const credentialTypes = {
});
}
console.log(data.fields);
const credentialType = await prisma.credentialType.create({
data: {
name: data.name,
@@ -91,6 +93,8 @@ export const credentialTypes = {
},
});
console.log(credentialType.fields);
return new CredentialTypeController(credentialType);
},
+2
View File
@@ -73,6 +73,7 @@ export const credentials = {
*/
async create(data: {
name: string;
notes?: string;
typeId: string;
companyId: string;
fields: {
@@ -131,6 +132,7 @@ export const credentials = {
const credential = await prisma.credential.create({
data: {
name: data.name,
notes: data.notes,
typeId: data.typeId,
companyId: data.companyId,
fields: fieldsObject,
+27 -4
View File
@@ -106,8 +106,31 @@ export const users = {
return controller;
},
};
/**
* @TODO Figure out default permissions
*/
/**
* Fetch all users
*
* Returns an array of UserController instances for every user in the database.
*
* @returns {Promise<UserController[]>} Array of user controllers
*/
async fetchAllUsers(): Promise<UserController[]> {
const allUsers = await prisma.user.findMany({
include: { roles: true },
});
return allUsers.map((u) => new UserController(u));
},
/**
* Delete a user
*
* Removes a user record from the database by their id.
*
* @param userId - The id of the user to delete
*/
async deleteUser(userId: string): Promise<void> {
await prisma.user.delete({ where: { id: userId } });
events.emit("user:deleted", { id: userId });
},
};
@@ -1,5 +1,9 @@
export enum ValueType {
PLAIN_TEXT = "plain_text",
LICENSE_KEY = "license_key",
IP_ADDRESS = "ip_address",
GENERIC_SECRET = "generic_secret",
BITLOCKER_KEY = "bitlocker_key",
PASSWORD = "password",
}
@@ -12,7 +16,6 @@ export interface CredentialTypeField {
}
export interface CredentialField {
id: string; // CUID
fieldId: string; // I.e. "clientId", "clientSecret", etc.
value: string; // Encrypted value stored in the database
}
@@ -19,7 +19,6 @@ export const fieldValidator = async (
acceptableFields: CredentialTypeField[],
): Promise<
{
id: string;
fieldId: string;
value: string;
secure: boolean;
@@ -47,7 +46,6 @@ export const fieldValidator = async (
const matchingField = afCollection.get(field.fieldId)!;
return {
id: field.id,
fieldId: field.fieldId,
value: field.value,
secure: matchingField.secure,
@@ -6,10 +6,13 @@ export const generateSecureValue = (content: string) => {
// Generate a hash of the content
const hash = Password.hash(content);
// Parse the PKCS#1 PEM key into a proper KeyObject
const publicKey = crypto.createPublicKey(secureValuesPublicKey);
// Encrypt the content using the .secureValues.pub public key
const encrypted = crypto.publicEncrypt(
{
key: secureValuesPublicKey,
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
+25 -9
View File
@@ -1,20 +1,36 @@
import Password from "../tools/Password";
import crypto from "crypto";
import { secureValuesPrivateKey } from "../../constants";
import GenericError from "../../Errors/GenericError";
const privateKey = crypto.createPrivateKey(secureValuesPrivateKey);
export const readSecureValue = (
encryptedContent: string,
hash?: string,
): string => {
// Decrypt the content using the .secureValues.key private key
const decrypted = crypto.privateDecrypt(
{
key: secureValuesPrivateKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
Buffer.from(encryptedContent, "base64"),
);
let decrypted: Buffer;
try {
// Decrypt the content using the .secureValues.key private key
decrypted = crypto.privateDecrypt(
{
key: privateKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
Buffer.from(encryptedContent, "base64"),
);
} catch {
throw new GenericError({
name: "SecureValueDecryptionError",
message:
"Unable to decrypt secure value. The value was encrypted with a different key and must be re-entered.",
cause:
"RSA key mismatch — the current private key does not match the public key used to encrypt this value.",
status: 422,
});
}
const content = decrypted.toString("utf-8");
+1
View File
@@ -19,6 +19,7 @@ interface EventTypes {
user: UserController;
updatedValues: Partial<User>;
}) => void;
"user:deleted": (data: { id: string }) => void;
"user:authenticated": (data: {
user: UserController;
tokens: SessionTokensObject;
+41 -1
View File
@@ -114,7 +114,10 @@ export const PERMISSION_NODES = {
{
node: "credential.secure_values.read",
description: "Read secure values of a credential",
usedIn: ["src/api/credentials/readSecureValues.ts"],
usedIn: [
"src/api/credentials/readSecureValues.ts",
"src/api/credentials/readSecureValue.ts",
],
dependencies: ["credential.fetch"],
},
],
@@ -231,6 +234,43 @@ export const PERMISSION_NODES = {
description: "Update user information",
usedIn: ["src/api/user/@me/update.ts"],
},
{
node: "user.read.other",
description: "Read other users' information",
usedIn: [
"src/api/user/fetch.ts",
"src/api/user/fetchRoles.ts",
"src/api/user/checkPermission.ts",
],
},
{
node: "user.list.other",
description: "List all users",
usedIn: ["src/api/user/fetchAll.ts"],
dependencies: ["user.read.other"],
},
{
node: "user.write.other",
description: "Update other users' information",
usedIn: ["src/api/user/update.ts"],
},
{
node: "user.roles.other",
description: "Modify roles assigned to other users",
usedIn: ["src/api/user/update.ts"],
dependencies: ["user.write.other"],
},
{
node: "user.permissions.other",
description: "Modify direct permissions assigned to other users",
usedIn: ["src/api/user/update.ts"],
dependencies: ["user.write.other"],
},
{
node: "user.delete.other",
description: "Delete other users",
usedIn: ["src/api/user/delete.ts"],
},
],
},
+3 -6
View File
@@ -40,14 +40,11 @@ await Promise.all(
});
if (!privExists || !pubExists) {
// Always regenerate both files together to ensure the key pair matches
console.log(`Generating '${v}' and '${pubPath}'...`);
const keys = keypair({ bits: 4096 });
if (!privExists) {
await Bun.write(v, keys.private);
}
if (!pubExists) {
await Bun.write(pubPath, keys.public);
}
await Bun.write(v, keys.private);
await Bun.write(pubPath, keys.public);
}
return;
}),