@joshuanode/n8n-nodes-autoelevate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/credentials/AutoElevateApi.credentials.d.ts +8 -0
- package/dist/credentials/AutoElevateApi.credentials.js +68 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/nodes/AutoElevate/AutoElevate.node.d.ts +17 -0
- package/dist/nodes/AutoElevate/AutoElevate.node.js +315 -0
- package/dist/nodes/AutoElevate/GenericFunctions.d.ts +25 -0
- package/dist/nodes/AutoElevate/GenericFunctions.js +169 -0
- package/dist/nodes/AutoElevate/autoElevate.dark.svg +7 -0
- package/dist/nodes/AutoElevate/autoElevate.png +0 -0
- package/dist/nodes/AutoElevate/autoElevate.svg +7 -0
- package/dist/nodes/AutoElevate/descriptions/Common.d.ts +7 -0
- package/dist/nodes/AutoElevate/descriptions/Common.js +124 -0
- package/dist/nodes/AutoElevate/descriptions/index.d.ts +2 -0
- package/dist/nodes/AutoElevate/descriptions/index.js +200 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joshua Smith
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# n8n-nodes-autoelevate
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@joshuanode/n8n-nodes-autoelevate)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://n8n.io/)
|
|
8
|
+
[](https://github.com/ajoshuasmith/n8n-nodes-autoelevate)
|
|
9
|
+
[](https://partner-api-docs.autoelevate.com/)
|
|
10
|
+
|
|
11
|
+
An n8n community node for the [AutoElevate Partner API](https://partner-api-docs.autoelevate.com/), providing MSP automation for endpoint inventory, elevation activity, approval workflows, and partner usage.
|
|
12
|
+
|
|
13
|
+
[n8n](https://n8n.io/) is a workflow automation platform that lets you connect AutoElevate to your PSA, reporting, security, and notification workflows.
|
|
14
|
+
|
|
15
|
+
## AutoElevate API Beta Status
|
|
16
|
+
|
|
17
|
+
> **Beta notice:** AutoElevate identifies its [Partner API](https://partner-api-docs.autoelevate.com/) as **beta** and states that it may change without notice. This node pins requests to `/api/v1` and automatically sends the mandatory beta acknowledgment header required by the API. Test workflows before using approval or denial actions in production.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Complete coverage of all endpoints published in the AutoElevate Partner API v1 specification
|
|
22
|
+
- API Token (`AE-BEARER`) and signed HMAC (`AE-HMAC-SHA256`) authentication
|
|
23
|
+
- Searchable company, computer, location, elevated-session, and elevation-request selectors
|
|
24
|
+
- Automatic offset and cursor pagination
|
|
25
|
+
- Picker searches that scan all available API pages when AutoElevate has no server-side search parameter
|
|
26
|
+
- Automatic `429` retries honoring `Retry-After`
|
|
27
|
+
- Server-side company, location, computer, status, approval-state, action, entity-type, and date filters
|
|
28
|
+
- n8n item pairing and **Continue On Fail** support
|
|
29
|
+
- Actionable errors for authentication, permissions, missing records, and rate limits
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Follow the [n8n community-node installation guide](https://docs.n8n.io/integrations/community-nodes/installation/).
|
|
34
|
+
|
|
35
|
+
### Community Nodes
|
|
36
|
+
|
|
37
|
+
1. In n8n, go to **Settings → Community Nodes**.
|
|
38
|
+
2. Select **Install**.
|
|
39
|
+
3. Enter `@joshuanode/n8n-nodes-autoelevate`.
|
|
40
|
+
4. Accept the community-node risk notice and install the package.
|
|
41
|
+
|
|
42
|
+
### Manual Installation
|
|
43
|
+
|
|
44
|
+
In a self-hosted n8n installation:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install @joshuanode/n8n-nodes-autoelevate
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Restart n8n after installation if the node does not appear immediately.
|
|
51
|
+
|
|
52
|
+
## Prerequisites
|
|
53
|
+
|
|
54
|
+
- An AutoElevate partner account with access to the Partner API
|
|
55
|
+
- An AutoElevate service user
|
|
56
|
+
- An API key with the permissions required by your workflows
|
|
57
|
+
- n8n 1.0 or newer
|
|
58
|
+
- Node.js 18.17.0 or newer for self-hosted installations
|
|
59
|
+
|
|
60
|
+
## Credentials
|
|
61
|
+
|
|
62
|
+
In the AutoElevate admin portal, open a **service user** and create an API key. Grant only the permissions needed by your workflows.
|
|
63
|
+
|
|
64
|
+
### API Token (recommended)
|
|
65
|
+
|
|
66
|
+
1. Create a key using the **API Token (AE-BEARER)** scheme.
|
|
67
|
+
2. Copy the `aeb_` token when it is displayed.
|
|
68
|
+
3. In n8n, create an **AutoElevate Partner API** credential.
|
|
69
|
+
4. Select **API Token (AE-BEARER)** and paste the token.
|
|
70
|
+
|
|
71
|
+
### HMAC
|
|
72
|
+
|
|
73
|
+
1. Create a key using the **HMAC (AE-HMAC-SHA256)** scheme.
|
|
74
|
+
2. Copy both the `aeh_` wire token and the separate HMAC signing key.
|
|
75
|
+
3. In n8n, select **HMAC (AE-HMAC-SHA256)** and enter both values.
|
|
76
|
+
|
|
77
|
+
AutoElevate displays secrets only once. Store them securely and revoke any key that may have been exposed.
|
|
78
|
+
|
|
79
|
+
## Resources and Operations
|
|
80
|
+
|
|
81
|
+
| Resource | Operations | Filters |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| Audit Log | Get Many | Entity type, action, start, end |
|
|
84
|
+
| Company | Get, Get Many | — |
|
|
85
|
+
| Computer | Get, Get Many | Company, location |
|
|
86
|
+
| Elevated Session | Get, Get Many | Company, computer, status |
|
|
87
|
+
| Elevation Event | Get Many | Company, start, end |
|
|
88
|
+
| Elevation Request | Approve, Deny, Get, Get Many | Company, approval state, start, end |
|
|
89
|
+
| Elevation Rule | Get Many | Company |
|
|
90
|
+
| Location | Get, Get Many | Company |
|
|
91
|
+
| Usage | Get | — |
|
|
92
|
+
|
|
93
|
+
## Example Workflows
|
|
94
|
+
|
|
95
|
+
### Notify on Pending Elevation Requests
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
Resource: Elevation Request
|
|
99
|
+
Operation: Get Many
|
|
100
|
+
Approval State: Pending
|
|
101
|
+
Return All: true
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Run this on a schedule, then route each request to Slack, Teams, email, or your PSA.
|
|
105
|
+
|
|
106
|
+
### Approve a Request
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
Resource: Elevation Request
|
|
110
|
+
Operation: Approve
|
|
111
|
+
Elevation Request: Select from list or provide a UUID
|
|
112
|
+
Elevation Type: Admin
|
|
113
|
+
Create Rule: false
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Enable **Create Rule** only when you intentionally want future matching requests handled automatically.
|
|
117
|
+
|
|
118
|
+
### Inventory Computers for a Company
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
Resource: Computer
|
|
122
|
+
Operation: Get Many
|
|
123
|
+
Company: Select from list
|
|
124
|
+
Return All: true
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## API Limits
|
|
128
|
+
|
|
129
|
+
During beta, AutoElevate documents a default limit of 100 requests per hour for each MSP, HTTP method, and route path. **Get Many** uses pages of up to 200 records, so large result sets consume multiple requests. A `429` response includes a `Retry-After` value.
|
|
130
|
+
|
|
131
|
+
## Error Handling and Troubleshooting
|
|
132
|
+
|
|
133
|
+
| Error | Meaning | Recommended action |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| `400 Bad Request` | Invalid parameters or a changed beta API contract | Review the node fields and compare the request with the current AutoElevate documentation |
|
|
136
|
+
| `401 Unauthorized` | Invalid, expired, or incorrectly configured API credentials | Confirm the selected authentication scheme and rotate the API key if needed |
|
|
137
|
+
| `403 Forbidden` | The service user or API key lacks permission for the operation | Add the required API permission in AutoElevate or use a properly scoped service user |
|
|
138
|
+
| `404 Not Found` | The record does not exist or is outside the partner account | Verify the selected company, computer, location, session, request, or rule |
|
|
139
|
+
| `429 Too Many Requests` | The route-specific API limit has been reached | The node retries twice using `Retry-After`; reduce polling frequency if the limit persists |
|
|
140
|
+
|
|
141
|
+
AutoElevate error messages and error codes are preserved in n8n when the API returns them. For example, a missing audit-log permission should surface AutoElevate's original authorization message rather than a generic request failure.
|
|
142
|
+
|
|
143
|
+
### Node Does Not Appear
|
|
144
|
+
|
|
145
|
+
- Restart n8n after installing the package.
|
|
146
|
+
- Confirm `@joshuanode/n8n-nodes-autoelevate` appears under **Settings → Community Nodes**.
|
|
147
|
+
- For Docker deployments, confirm the package is installed in the same environment and user context that runs n8n.
|
|
148
|
+
|
|
149
|
+
### Picker Search Is Slow
|
|
150
|
+
|
|
151
|
+
The Partner API does not currently expose server-side search parameters for picker resources. When search text is entered, the node scans all available API pages and filters the results locally. Large partner accounts may therefore consume several requests while searching.
|
|
152
|
+
|
|
153
|
+
## Compatibility
|
|
154
|
+
|
|
155
|
+
- n8n: 1.0 or newer
|
|
156
|
+
- Node.js: 18.17.0 or newer
|
|
157
|
+
- AutoElevate Partner API: v1 beta
|
|
158
|
+
|
|
159
|
+
## Development
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm install
|
|
163
|
+
npm run build
|
|
164
|
+
npm run lint
|
|
165
|
+
npm test
|
|
166
|
+
npm pack --dry-run
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
For local n8n testing, use `npm link` or install the generated package tarball in your test instance.
|
|
170
|
+
|
|
171
|
+
> **Workflow type warning:** Loading the node directly from n8n's custom-node directory registers it as `CUSTOM.autoElevate`. Installing the published package registers it as `@joshuanode/n8n-nodes-autoelevate.autoElevate`. Workflows created with the custom-directory build must have that node type migrated before they can use the npm-installed package. For durable test workflows, install the generated package tarball instead of loading the source directory as a custom node.
|
|
172
|
+
|
|
173
|
+
## Security
|
|
174
|
+
|
|
175
|
+
- Use a dedicated AutoElevate service user.
|
|
176
|
+
- Prefer short-lived API keys for testing.
|
|
177
|
+
- Scope keys to least privilege.
|
|
178
|
+
- Never commit API tokens or HMAC signing keys.
|
|
179
|
+
- Treat elevation approval and rule creation as privileged actions.
|
|
180
|
+
|
|
181
|
+
Please report security issues privately rather than opening a public issue containing credentials or customer data.
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
Bug reports, feature requests, and pull requests are welcome in the [GitHub repository](https://github.com/ajoshuasmith/n8n-nodes-autoelevate).
|
|
186
|
+
|
|
187
|
+
## Support
|
|
188
|
+
|
|
189
|
+
- For node bugs or feature requests, open a [GitHub issue](https://github.com/ajoshuasmith/n8n-nodes-autoelevate/issues).
|
|
190
|
+
- For AutoElevate account access, API permissions, or platform behavior, contact AutoElevate support.
|
|
191
|
+
- Do not include API keys, HMAC signing keys, customer names, computer names, or other sensitive tenant data in public issues.
|
|
192
|
+
|
|
193
|
+
## Version History
|
|
194
|
+
|
|
195
|
+
### 0.1.0
|
|
196
|
+
|
|
197
|
+
- Initial public release
|
|
198
|
+
- Complete coverage of the currently published AutoElevate Partner API v1 beta endpoints
|
|
199
|
+
- Bearer and HMAC authentication
|
|
200
|
+
- Searchable resource selectors, pagination, filtering, rate-limit retries, and preserved API errors
|
|
201
|
+
|
|
202
|
+
## Resources
|
|
203
|
+
|
|
204
|
+
- [AutoElevate Partner API documentation](https://partner-api-docs.autoelevate.com/)
|
|
205
|
+
- [n8n Community Nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
|
|
206
|
+
- [n8n Community Nodes installation guide](https://docs.n8n.io/integrations/community-nodes/installation/)
|
|
207
|
+
|
|
208
|
+
## Author
|
|
209
|
+
|
|
210
|
+
Joshua Smith
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
[MIT](LICENSE)
|
|
215
|
+
|
|
216
|
+
## Disclaimer
|
|
217
|
+
|
|
218
|
+
This is an independent community integration and is not an official AutoElevate product. AutoElevate is a trademark of its respective owner. Use of the AutoElevate Partner API remains subject to AutoElevate's terms, permissions, rate limits, and beta API conditions.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class AutoElevateApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: "file:../nodes/AutoElevate/autoElevate.svg";
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoElevateApi = void 0;
|
|
4
|
+
class AutoElevateApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'autoElevateApi';
|
|
7
|
+
this.displayName = 'AutoElevate Partner API';
|
|
8
|
+
this.icon = 'file:../nodes/AutoElevate/autoElevate.svg';
|
|
9
|
+
this.documentationUrl = 'https://partner-api-docs.autoelevate.com/';
|
|
10
|
+
this.properties = [
|
|
11
|
+
{
|
|
12
|
+
displayName: 'Authentication',
|
|
13
|
+
name: 'authentication',
|
|
14
|
+
type: 'options',
|
|
15
|
+
options: [
|
|
16
|
+
{
|
|
17
|
+
name: 'API Token (AE-BEARER)',
|
|
18
|
+
value: 'bearer',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'HMAC (AE-HMAC-SHA256)',
|
|
22
|
+
value: 'hmac',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
default: 'bearer',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
displayName: 'API Token',
|
|
29
|
+
name: 'apiToken',
|
|
30
|
+
type: 'string',
|
|
31
|
+
typeOptions: { password: true },
|
|
32
|
+
default: '',
|
|
33
|
+
required: true,
|
|
34
|
+
displayOptions: { show: { authentication: ['bearer'] } },
|
|
35
|
+
description: 'The aeb_ token shown when an AE-BEARER API key is created',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
displayName: 'HMAC Token',
|
|
39
|
+
name: 'hmacToken',
|
|
40
|
+
type: 'string',
|
|
41
|
+
typeOptions: { password: true },
|
|
42
|
+
default: '',
|
|
43
|
+
required: true,
|
|
44
|
+
displayOptions: { show: { authentication: ['hmac'] } },
|
|
45
|
+
description: 'The aeh_ wire token shown when an AE-HMAC-SHA256 API key is created',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
displayName: 'HMAC Signing Key',
|
|
49
|
+
name: 'hmacKey',
|
|
50
|
+
type: 'string',
|
|
51
|
+
typeOptions: { password: true },
|
|
52
|
+
default: '',
|
|
53
|
+
required: true,
|
|
54
|
+
displayOptions: { show: { authentication: ['hmac'] } },
|
|
55
|
+
description: 'The separate signing key shown once when the HMAC API key is created',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
displayName: 'Base URL',
|
|
59
|
+
name: 'baseUrl',
|
|
60
|
+
type: 'string',
|
|
61
|
+
default: 'https://partner-api.autoelevate.com',
|
|
62
|
+
required: true,
|
|
63
|
+
description: 'Partner API base URL. Change only when AutoElevate provides another environment.',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.AutoElevateApi = AutoElevateApi;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./credentials/AutoElevateApi.credentials"), exports);
|
|
18
|
+
__exportStar(require("./nodes/AutoElevate/AutoElevate.node"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ICredentialDataDecryptedObject, ICredentialsDecrypted, ICredentialTestFunctions, IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodeListSearchResult, INodeCredentialTestResult, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class AutoElevate implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
methods: {
|
|
5
|
+
credentialTest: {
|
|
6
|
+
autoElevateApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted<ICredentialDataDecryptedObject>): Promise<INodeCredentialTestResult>;
|
|
7
|
+
};
|
|
8
|
+
listSearch: {
|
|
9
|
+
searchCompanies(this: ILoadOptionsFunctions, filter?: string, paginationToken?: string): Promise<INodeListSearchResult>;
|
|
10
|
+
searchComputers(this: ILoadOptionsFunctions, filter?: string, paginationToken?: string): Promise<INodeListSearchResult>;
|
|
11
|
+
searchElevatedSessions(this: ILoadOptionsFunctions, filter?: string, paginationToken?: string): Promise<INodeListSearchResult>;
|
|
12
|
+
searchElevationRequests(this: ILoadOptionsFunctions, filter?: string, paginationToken?: string): Promise<INodeListSearchResult>;
|
|
13
|
+
searchLocations(this: ILoadOptionsFunctions, filter?: string, paginationToken?: string): Promise<INodeListSearchResult>;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoElevate = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const GenericFunctions_1 = require("./GenericFunctions");
|
|
6
|
+
const descriptions_1 = require("./descriptions");
|
|
7
|
+
function dateToEpoch(value) {
|
|
8
|
+
if (!value)
|
|
9
|
+
return undefined;
|
|
10
|
+
const epoch = Date.parse(value);
|
|
11
|
+
return Number.isNaN(epoch) ? undefined : epoch;
|
|
12
|
+
}
|
|
13
|
+
function optionalLocator(context, name, index) {
|
|
14
|
+
try {
|
|
15
|
+
return (0, GenericFunctions_1.unwrapLocator)(context.getNodeParameter(name, index, ''));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function getList(context, itemIndex, endpoint, query, cursorPagination = false) {
|
|
22
|
+
const returnAll = context.getNodeParameter('returnAll', itemIndex);
|
|
23
|
+
const limit = returnAll
|
|
24
|
+
? Number.POSITIVE_INFINITY
|
|
25
|
+
: context.getNodeParameter('limit', itemIndex);
|
|
26
|
+
return GenericFunctions_1.autoElevateApiRequestAllItems.call(context, endpoint, query, cursorPagination, limit);
|
|
27
|
+
}
|
|
28
|
+
async function searchResource(resource, filter, paginationToken) {
|
|
29
|
+
const take = 100;
|
|
30
|
+
const skip = Math.max(0, Number.parseInt(paginationToken ?? '0', 10) || 0);
|
|
31
|
+
const query = { take };
|
|
32
|
+
query.skip = skip;
|
|
33
|
+
if (resource === 'elevation-requests')
|
|
34
|
+
query.approvalState = 'PENDING';
|
|
35
|
+
const normalizedFilter = filter?.trim().toLowerCase();
|
|
36
|
+
let items;
|
|
37
|
+
let hasMore = false;
|
|
38
|
+
if (normalizedFilter) {
|
|
39
|
+
items = await GenericFunctions_1.autoElevateApiRequestAllItems.call(this, `/api/v1/${resource}`, resource === 'elevation-requests' ? { approvalState: 'PENDING' } : {});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const response = (await GenericFunctions_1.autoElevateApiRequest.call(this, 'GET', `/api/v1/${resource}`, {}, query));
|
|
43
|
+
items = response.items ?? [];
|
|
44
|
+
hasMore =
|
|
45
|
+
(response.totalCount !== undefined && skip + take < response.totalCount) ||
|
|
46
|
+
response.hasMore === true;
|
|
47
|
+
}
|
|
48
|
+
items = items.filter((item) => {
|
|
49
|
+
if (!normalizedFilter)
|
|
50
|
+
return true;
|
|
51
|
+
return Object.values(item).some((value) => typeof value === 'string' && value.toLowerCase().includes(normalizedFilter));
|
|
52
|
+
});
|
|
53
|
+
const results = items.map((item) => {
|
|
54
|
+
const id = String(item.id);
|
|
55
|
+
let name = String(item.name ?? item.machineName ?? item.targetDescription ?? id);
|
|
56
|
+
if (resource === 'elevated-sessions') {
|
|
57
|
+
name = `${String(item.userName ?? 'Unknown user')} — ${String(item.status ?? '')} (${id})`;
|
|
58
|
+
}
|
|
59
|
+
if (resource === 'elevation-requests') {
|
|
60
|
+
name = `${String(item.targetDescription ?? 'Elevation request')} — ${String(item.requestedBy ?? 'Unknown user')}`;
|
|
61
|
+
}
|
|
62
|
+
return { name, value: id };
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
results,
|
|
66
|
+
...(hasMore ? { paginationToken: String(skip + take) } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
class AutoElevate {
|
|
70
|
+
constructor() {
|
|
71
|
+
this.description = {
|
|
72
|
+
displayName: 'AutoElevate',
|
|
73
|
+
name: 'autoElevate',
|
|
74
|
+
icon: {
|
|
75
|
+
light: 'file:autoElevate.svg',
|
|
76
|
+
dark: 'file:autoElevate.dark.svg',
|
|
77
|
+
},
|
|
78
|
+
group: ['transform'],
|
|
79
|
+
version: 1,
|
|
80
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
81
|
+
description: 'Access inventory, security events, elevation requests, and usage in AutoElevate',
|
|
82
|
+
defaults: { name: 'AutoElevate' },
|
|
83
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
84
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
85
|
+
usableAsTool: true,
|
|
86
|
+
credentials: [
|
|
87
|
+
{
|
|
88
|
+
name: 'autoElevateApi',
|
|
89
|
+
required: true,
|
|
90
|
+
testedBy: 'autoElevateApiCredentialTest',
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
properties: [
|
|
94
|
+
{
|
|
95
|
+
displayName: 'The AutoElevate Partner API is in beta and may change. This node sends the required beta acknowledgment with every request.',
|
|
96
|
+
name: 'betaNotice',
|
|
97
|
+
type: 'notice',
|
|
98
|
+
default: '',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
displayName: 'Resource',
|
|
102
|
+
name: 'resource',
|
|
103
|
+
type: 'options',
|
|
104
|
+
noDataExpression: true,
|
|
105
|
+
options: [
|
|
106
|
+
{ name: 'Audit Log', value: 'auditLog' },
|
|
107
|
+
{ name: 'Company', value: 'company' },
|
|
108
|
+
{ name: 'Computer', value: 'computer' },
|
|
109
|
+
{ name: 'Elevated Session', value: 'elevatedSession' },
|
|
110
|
+
{ name: 'Elevation Event', value: 'elevationEvent' },
|
|
111
|
+
{ name: 'Elevation Request', value: 'elevationRequest' },
|
|
112
|
+
{ name: 'Elevation Rule', value: 'elevationRule' },
|
|
113
|
+
{ name: 'Location', value: 'location' },
|
|
114
|
+
{ name: 'Usage', value: 'usage' },
|
|
115
|
+
],
|
|
116
|
+
default: 'elevationRequest',
|
|
117
|
+
},
|
|
118
|
+
...descriptions_1.autoElevateProperties,
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
this.methods = {
|
|
122
|
+
credentialTest: {
|
|
123
|
+
async autoElevateApiCredentialTest(credential) {
|
|
124
|
+
const credentials = credential.data;
|
|
125
|
+
if (!credentials) {
|
|
126
|
+
return { status: 'Error', message: 'AutoElevate credentials are incomplete' };
|
|
127
|
+
}
|
|
128
|
+
const url = `${String(credentials.baseUrl).replace(/\/+$/, '')}/api/v1/usage`;
|
|
129
|
+
const headers = {
|
|
130
|
+
Accept: 'application/json',
|
|
131
|
+
'X-Acknowledgment': 'i-understand-this-is-beta-and-may-change',
|
|
132
|
+
};
|
|
133
|
+
if (credentials.authentication === 'hmac') {
|
|
134
|
+
headers.Authorization = (0, GenericFunctions_1.createHmacAuthorization)('GET', url, '', String(credentials.hmacToken), String(credentials.hmacKey), Date.now().toString());
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
headers.Authorization = `Bearer ${String(credentials.apiToken)}`;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const response = await fetch(url, { method: 'GET', headers });
|
|
141
|
+
if (response.ok)
|
|
142
|
+
return { status: 'OK', message: 'Connection successful' };
|
|
143
|
+
const responseBody = (await response.json().catch(() => ({})));
|
|
144
|
+
return {
|
|
145
|
+
status: 'Error',
|
|
146
|
+
message: typeof responseBody.message === 'string'
|
|
147
|
+
? responseBody.message
|
|
148
|
+
: `AutoElevate credential test failed with HTTP ${response.status}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const details = (0, GenericFunctions_1.getAutoElevateErrorDetails)(error);
|
|
153
|
+
return {
|
|
154
|
+
status: 'Error',
|
|
155
|
+
message: details.responseMessage ?? 'Unable to connect to AutoElevate',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
listSearch: {
|
|
161
|
+
searchCompanies(filter, paginationToken) {
|
|
162
|
+
return searchResource.call(this, 'companies', filter, paginationToken);
|
|
163
|
+
},
|
|
164
|
+
searchComputers(filter, paginationToken) {
|
|
165
|
+
return searchResource.call(this, 'computers', filter, paginationToken);
|
|
166
|
+
},
|
|
167
|
+
searchElevatedSessions(filter, paginationToken) {
|
|
168
|
+
return searchResource.call(this, 'elevated-sessions', filter, paginationToken);
|
|
169
|
+
},
|
|
170
|
+
searchElevationRequests(filter, paginationToken) {
|
|
171
|
+
return searchResource.call(this, 'elevation-requests', filter, paginationToken);
|
|
172
|
+
},
|
|
173
|
+
searchLocations(filter, paginationToken) {
|
|
174
|
+
return searchResource.call(this, 'locations', filter, paginationToken);
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async execute() {
|
|
180
|
+
const inputItems = this.getInputData();
|
|
181
|
+
const returnData = [];
|
|
182
|
+
for (let itemIndex = 0; itemIndex < inputItems.length; itemIndex++) {
|
|
183
|
+
try {
|
|
184
|
+
const resource = this.getNodeParameter('resource', itemIndex);
|
|
185
|
+
const operation = this.getNodeParameter('operation', itemIndex);
|
|
186
|
+
let response;
|
|
187
|
+
if (resource === 'auditLog') {
|
|
188
|
+
const query = {
|
|
189
|
+
entityType: this.getNodeParameter('entityType', itemIndex, ''),
|
|
190
|
+
action: this.getNodeParameter('auditAction', itemIndex, ''),
|
|
191
|
+
start: dateToEpoch(this.getNodeParameter('start', itemIndex, '')),
|
|
192
|
+
end: dateToEpoch(this.getNodeParameter('end', itemIndex, '')),
|
|
193
|
+
};
|
|
194
|
+
response = await getList(this, itemIndex, '/api/v1/audit-logs', query, true);
|
|
195
|
+
}
|
|
196
|
+
else if (resource === 'company') {
|
|
197
|
+
if (operation === 'get') {
|
|
198
|
+
const id = (0, GenericFunctions_1.unwrapLocator)(this.getNodeParameter('companyId', itemIndex));
|
|
199
|
+
response = await GenericFunctions_1.autoElevateApiRequest.call(this, 'GET', `/api/v1/companies/${id}`);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
response = await getList(this, itemIndex, '/api/v1/companies', {});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (resource === 'computer') {
|
|
206
|
+
if (operation === 'get') {
|
|
207
|
+
const id = (0, GenericFunctions_1.unwrapLocator)(this.getNodeParameter('computerId', itemIndex));
|
|
208
|
+
response = await GenericFunctions_1.autoElevateApiRequest.call(this, 'GET', `/api/v1/computers/${id}`);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
response = await getList(this, itemIndex, '/api/v1/computers', {
|
|
212
|
+
companyId: optionalLocator(this, 'companyId', itemIndex),
|
|
213
|
+
locationId: optionalLocator(this, 'locationId', itemIndex),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (resource === 'elevatedSession') {
|
|
218
|
+
if (operation === 'get') {
|
|
219
|
+
const id = (0, GenericFunctions_1.unwrapLocator)(this.getNodeParameter('elevatedSessionId', itemIndex));
|
|
220
|
+
response = await GenericFunctions_1.autoElevateApiRequest.call(this, 'GET', `/api/v1/elevated-sessions/${id}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
response = await getList(this, itemIndex, '/api/v1/elevated-sessions', {
|
|
224
|
+
companyId: optionalLocator(this, 'companyId', itemIndex),
|
|
225
|
+
computerId: optionalLocator(this, 'computerId', itemIndex),
|
|
226
|
+
status: this.getNodeParameter('status', itemIndex, ''),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else if (resource === 'elevationEvent') {
|
|
231
|
+
response = await getList(this, itemIndex, '/api/v1/elevation-events', {
|
|
232
|
+
start: dateToEpoch(this.getNodeParameter('start', itemIndex, '')),
|
|
233
|
+
end: dateToEpoch(this.getNodeParameter('end', itemIndex, '')),
|
|
234
|
+
companyId: optionalLocator(this, 'companyId', itemIndex),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
else if (resource === 'elevationRequest') {
|
|
238
|
+
if (operation === 'get') {
|
|
239
|
+
const id = (0, GenericFunctions_1.unwrapLocator)(this.getNodeParameter('elevationRequestId', itemIndex));
|
|
240
|
+
response = await GenericFunctions_1.autoElevateApiRequest.call(this, 'GET', `/api/v1/elevation-requests/${id}`);
|
|
241
|
+
}
|
|
242
|
+
else if (operation === 'approve' || operation === 'deny') {
|
|
243
|
+
const id = (0, GenericFunctions_1.unwrapLocator)(this.getNodeParameter('elevationRequestId', itemIndex));
|
|
244
|
+
const createRule = this.getNodeParameter('createRule', itemIndex);
|
|
245
|
+
const body = { createRule };
|
|
246
|
+
if (createRule) {
|
|
247
|
+
body.ruleLevel = this.getNodeParameter('ruleLevel', itemIndex);
|
|
248
|
+
}
|
|
249
|
+
if (operation === 'approve') {
|
|
250
|
+
const elevationType = this.getNodeParameter('elevationType', itemIndex, '');
|
|
251
|
+
const duration = this.getNodeParameter('durationInMinutes', itemIndex, 0);
|
|
252
|
+
if (elevationType)
|
|
253
|
+
body.elevationType = elevationType;
|
|
254
|
+
if (duration > 0)
|
|
255
|
+
body.durationInMinutes = duration;
|
|
256
|
+
}
|
|
257
|
+
response = await GenericFunctions_1.autoElevateApiRequest.call(this, 'POST', `/api/v1/elevation-requests/${id}/${operation}`, body);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
response = await getList(this, itemIndex, '/api/v1/elevation-requests', {
|
|
261
|
+
start: dateToEpoch(this.getNodeParameter('start', itemIndex, '')),
|
|
262
|
+
end: dateToEpoch(this.getNodeParameter('end', itemIndex, '')),
|
|
263
|
+
companyId: optionalLocator(this, 'companyId', itemIndex),
|
|
264
|
+
approvalState: this.getNodeParameter('approvalState', itemIndex, ''),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else if (resource === 'elevationRule') {
|
|
269
|
+
response = await getList(this, itemIndex, '/api/v1/elevation-rules', {
|
|
270
|
+
companyId: optionalLocator(this, 'companyId', itemIndex),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
else if (resource === 'location') {
|
|
274
|
+
if (operation === 'get') {
|
|
275
|
+
const id = (0, GenericFunctions_1.unwrapLocator)(this.getNodeParameter('locationId', itemIndex));
|
|
276
|
+
response = await GenericFunctions_1.autoElevateApiRequest.call(this, 'GET', `/api/v1/locations/${id}`);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
response = await getList(this, itemIndex, '/api/v1/locations', {
|
|
280
|
+
companyId: optionalLocator(this, 'companyId', itemIndex),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else if (resource === 'usage') {
|
|
285
|
+
response = await GenericFunctions_1.autoElevateApiRequest.call(this, 'GET', '/api/v1/usage');
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported resource: ${resource}`);
|
|
289
|
+
}
|
|
290
|
+
const output = Array.isArray(response) ? response : [response];
|
|
291
|
+
returnData.push(...output.map((json) => ({
|
|
292
|
+
json,
|
|
293
|
+
pairedItem: { item: itemIndex },
|
|
294
|
+
})));
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
if (this.continueOnFail()) {
|
|
298
|
+
returnData.push({
|
|
299
|
+
json: { error: error instanceof Error ? error.message : String(error) },
|
|
300
|
+
pairedItem: { item: itemIndex },
|
|
301
|
+
});
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (error instanceof n8n_workflow_1.NodeApiError || error instanceof n8n_workflow_1.NodeOperationError) {
|
|
305
|
+
// The error is already normalized with n8n's node error classes.
|
|
306
|
+
// eslint-disable-next-line @n8n/community-nodes/require-node-api-error
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return [returnData];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
exports.AutoElevate = AutoElevate;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { IDataObject, IExecuteFunctions, IHttpRequestMethods, ILoadOptionsFunctions, JsonObject } from 'n8n-workflow';
|
|
2
|
+
export interface AutoElevateListResponse {
|
|
3
|
+
items: IDataObject[];
|
|
4
|
+
totalCount?: number;
|
|
5
|
+
nextCursor?: string | null;
|
|
6
|
+
hasMore?: boolean;
|
|
7
|
+
}
|
|
8
|
+
type AutoElevateContext = IExecuteFunctions | ILoadOptionsFunctions;
|
|
9
|
+
export interface AutoElevateErrorDetails {
|
|
10
|
+
statusCode: number;
|
|
11
|
+
responseBody: JsonObject;
|
|
12
|
+
responseMessage?: string;
|
|
13
|
+
retryAfter?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function createHmacAuthorization(method: string, url: string, serializedBody: string, token: string, hmacKey: string, timestamp: string): string;
|
|
16
|
+
export declare function getAutoElevateErrorDetails(error: unknown): AutoElevateErrorDetails;
|
|
17
|
+
export declare function parseRetryAfterMs(value: string | undefined, now?: number): number;
|
|
18
|
+
export declare function unwrapLocator(value: unknown): string;
|
|
19
|
+
export declare function autoElevateApiRequest(this: AutoElevateContext, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, query?: IDataObject): Promise<IDataObject>;
|
|
20
|
+
export declare function autoElevateApiRequestAllItems(this: AutoElevateContext, endpoint: string, query?: IDataObject, cursorPagination?: boolean, maxItems?: number): Promise<IDataObject[]>;
|
|
21
|
+
export declare const autoElevateConstants: {
|
|
22
|
+
acknowledgment: string;
|
|
23
|
+
emptyBodyHash: string;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.autoElevateConstants = void 0;
|
|
4
|
+
exports.createHmacAuthorization = createHmacAuthorization;
|
|
5
|
+
exports.getAutoElevateErrorDetails = getAutoElevateErrorDetails;
|
|
6
|
+
exports.parseRetryAfterMs = parseRetryAfterMs;
|
|
7
|
+
exports.unwrapLocator = unwrapLocator;
|
|
8
|
+
exports.autoElevateApiRequest = autoElevateApiRequest;
|
|
9
|
+
exports.autoElevateApiRequestAllItems = autoElevateApiRequestAllItems;
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
11
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
12
|
+
const ACKNOWLEDGMENT = 'i-understand-this-is-beta-and-may-change';
|
|
13
|
+
const EMPTY_BODY_HASH = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
|
|
14
|
+
function buildUrl(baseUrl, endpoint, query) {
|
|
15
|
+
const url = new URL(endpoint, `${baseUrl.replace(/\/+$/, '')}/`);
|
|
16
|
+
for (const [key, value] of Object.entries(query)) {
|
|
17
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
18
|
+
url.searchParams.set(key, String(value));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return url.toString();
|
|
22
|
+
}
|
|
23
|
+
function createHmacAuthorization(method, url, serializedBody, token, hmacKey, timestamp) {
|
|
24
|
+
const bodyHash = serializedBody
|
|
25
|
+
? (0, node_crypto_1.createHash)('sha256').update(serializedBody).digest('hex')
|
|
26
|
+
: EMPTY_BODY_HASH;
|
|
27
|
+
const stringToSign = ['AE-HMAC-SHA256', method.toUpperCase(), url, bodyHash, timestamp].join('\n');
|
|
28
|
+
const signature = (0, node_crypto_1.createHmac)('sha256', hmacKey).update(stringToSign).digest('hex');
|
|
29
|
+
return (`AE-HMAC-SHA256 token=${token},bodyHash=${bodyHash},` +
|
|
30
|
+
`ts=${timestamp},sig=${signature}`);
|
|
31
|
+
}
|
|
32
|
+
function asRecord(value) {
|
|
33
|
+
return value !== null && typeof value === 'object' ? value : {};
|
|
34
|
+
}
|
|
35
|
+
function getHeader(headers, name) {
|
|
36
|
+
const headerRecord = asRecord(headers);
|
|
37
|
+
const directValue = headerRecord[name] ?? headerRecord[name.toLowerCase()];
|
|
38
|
+
if (directValue !== undefined)
|
|
39
|
+
return String(directValue);
|
|
40
|
+
const get = headerRecord.get;
|
|
41
|
+
if (typeof get === 'function') {
|
|
42
|
+
// Must be invoked with the header object as `this`. Axios' AxiosHeaders.get
|
|
43
|
+
// does Object.keys(this) internally, so an unbound call throws TypeError and
|
|
44
|
+
// takes down the whole error handler.
|
|
45
|
+
const value = get.call(headerRecord, name);
|
|
46
|
+
return value === undefined || value === null ? undefined : String(value);
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function getAutoElevateErrorDetails(error) {
|
|
51
|
+
const errorRecord = asRecord(error);
|
|
52
|
+
const response = asRecord(errorRecord.response);
|
|
53
|
+
const responseBodyValue = response.data ?? response.body ?? errorRecord;
|
|
54
|
+
const responseBody = asRecord(responseBodyValue);
|
|
55
|
+
const responseMessageValue = responseBody.message ?? errorRecord.message;
|
|
56
|
+
return {
|
|
57
|
+
statusCode: Number(response.status ?? errorRecord.statusCode ?? errorRecord.httpCode ?? 0),
|
|
58
|
+
responseBody,
|
|
59
|
+
...(responseMessageValue ? { responseMessage: String(responseMessageValue) } : {}),
|
|
60
|
+
...(getHeader(response.headers, 'retry-after')
|
|
61
|
+
? { retryAfter: getHeader(response.headers, 'retry-after') }
|
|
62
|
+
: {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function parseRetryAfterMs(value, now = Date.now()) {
|
|
66
|
+
if (!value)
|
|
67
|
+
return 1000;
|
|
68
|
+
const seconds = Number(value);
|
|
69
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
70
|
+
return seconds * 1000;
|
|
71
|
+
const retryAt = Date.parse(value);
|
|
72
|
+
if (!Number.isNaN(retryAt))
|
|
73
|
+
return Math.max(0, retryAt - now);
|
|
74
|
+
return 1000;
|
|
75
|
+
}
|
|
76
|
+
function unwrapLocator(value) {
|
|
77
|
+
if (typeof value === 'string')
|
|
78
|
+
return value;
|
|
79
|
+
if (value && typeof value === 'object' && 'value' in value) {
|
|
80
|
+
return String(value.value);
|
|
81
|
+
}
|
|
82
|
+
return String(value ?? '');
|
|
83
|
+
}
|
|
84
|
+
async function autoElevateApiRequest(method, endpoint, body = {}, query = {}) {
|
|
85
|
+
const credentials = await this.getCredentials('autoElevateApi');
|
|
86
|
+
const url = buildUrl(String(credentials.baseUrl), endpoint, query);
|
|
87
|
+
const hasBody = Object.keys(body).length > 0;
|
|
88
|
+
const serializedBody = hasBody ? JSON.stringify(body) : '';
|
|
89
|
+
for (let attempt = 0; attempt <= 2; attempt++) {
|
|
90
|
+
const headers = {
|
|
91
|
+
Accept: 'application/json',
|
|
92
|
+
'X-Acknowledgment': ACKNOWLEDGMENT,
|
|
93
|
+
};
|
|
94
|
+
if (hasBody)
|
|
95
|
+
headers['Content-Type'] = 'application/json';
|
|
96
|
+
if (credentials.authentication === 'hmac') {
|
|
97
|
+
headers.Authorization = createHmacAuthorization(method, url, serializedBody, String(credentials.hmacToken), String(credentials.hmacKey), Date.now().toString());
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
headers.Authorization = `Bearer ${String(credentials.apiToken)}`;
|
|
101
|
+
}
|
|
102
|
+
const options = { method, url, headers, json: true };
|
|
103
|
+
if (hasBody)
|
|
104
|
+
options.body = serializedBody;
|
|
105
|
+
try {
|
|
106
|
+
// AutoElevate's HMAC scheme signs the exact URL and body bytes, which
|
|
107
|
+
// cannot be expressed with n8n's declarative credential authentication.
|
|
108
|
+
// eslint-disable-next-line @n8n/community-nodes/no-http-request-with-manual-auth
|
|
109
|
+
return (await this.helpers.httpRequest(options));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
const details = getAutoElevateErrorDetails(error);
|
|
113
|
+
if (details.statusCode === 429 && attempt < 2) {
|
|
114
|
+
await (0, n8n_workflow_1.sleep)(parseRetryAfterMs(details.retryAfter));
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const messages = {
|
|
118
|
+
400: 'AutoElevate rejected the request. Check the supplied fields and beta acknowledgment.',
|
|
119
|
+
401: 'AutoElevate authentication failed. Check the API key and authentication scheme.',
|
|
120
|
+
403: 'The API key does not have permission for this AutoElevate operation.',
|
|
121
|
+
404: 'The requested AutoElevate record was not found or is outside this partner account.',
|
|
122
|
+
429: 'AutoElevate rate limit reached after two retries.',
|
|
123
|
+
};
|
|
124
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), details.responseBody, {
|
|
125
|
+
message: details.responseMessage ??
|
|
126
|
+
messages[details.statusCode] ??
|
|
127
|
+
'AutoElevate API request failed',
|
|
128
|
+
description: messages[details.statusCode],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {}, { message: 'AutoElevate API request failed' });
|
|
133
|
+
}
|
|
134
|
+
async function autoElevateApiRequestAllItems(endpoint, query = {}, cursorPagination = false, maxItems = Number.POSITIVE_INFINITY) {
|
|
135
|
+
const results = [];
|
|
136
|
+
let skip = 0;
|
|
137
|
+
let cursor;
|
|
138
|
+
while (results.length < maxItems) {
|
|
139
|
+
const take = Math.min(200, maxItems - results.length);
|
|
140
|
+
const pageQuery = { ...query, take };
|
|
141
|
+
if (cursorPagination) {
|
|
142
|
+
if (cursor)
|
|
143
|
+
pageQuery.cursor = cursor;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
pageQuery.skip = skip;
|
|
147
|
+
}
|
|
148
|
+
const response = (await autoElevateApiRequest.call(this, 'GET', endpoint, {}, pageQuery));
|
|
149
|
+
const pageItems = Array.isArray(response.items) ? response.items : [];
|
|
150
|
+
results.push(...pageItems);
|
|
151
|
+
if (cursorPagination) {
|
|
152
|
+
if (!response.hasMore || !response.nextCursor || pageItems.length === 0)
|
|
153
|
+
break;
|
|
154
|
+
cursor = response.nextCursor;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
if (pageItems.length < take ||
|
|
158
|
+
(response.totalCount !== undefined && results.length >= response.totalCount)) {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
skip += pageItems.length;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return results.slice(0, maxItems);
|
|
165
|
+
}
|
|
166
|
+
exports.autoElevateConstants = {
|
|
167
|
+
acknowledgment: ACKNOWLEDGMENT,
|
|
168
|
+
emptyBodyHash: EMPTY_BODY_HASH,
|
|
169
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="AutoElevate">
|
|
2
|
+
<rect width="128" height="128" rx="18" fill="#161616"/>
|
|
3
|
+
<circle cx="64" cy="59" r="45" fill="none" stroke="#fff" stroke-width="10"/>
|
|
4
|
+
<path d="M89 85a34 34 0 1 1 8-48" fill="none" stroke="#ef4444" stroke-width="10" stroke-linecap="round"/>
|
|
5
|
+
<path d="M98 34a34 34 0 0 1-10 54c-15 9-34 1-38-16l-3-13" fill="none" stroke="#fff" stroke-width="10" stroke-linecap="round"/>
|
|
6
|
+
<path d="m45 65 8-26 13 24-9-2 3 12Z" fill="#fff"/>
|
|
7
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="AutoElevate">
|
|
2
|
+
<rect width="128" height="128" rx="18" fill="#fff"/>
|
|
3
|
+
<circle cx="64" cy="59" r="45" fill="none" stroke="#000" stroke-width="10"/>
|
|
4
|
+
<path d="M89 85a34 34 0 1 1 8-48" fill="none" stroke="#d72b2f" stroke-width="10" stroke-linecap="round"/>
|
|
5
|
+
<path d="M98 34a34 34 0 0 1-10 54c-15 9-34 1-38-16l-3-13" fill="none" stroke="#000" stroke-width="10" stroke-linecap="round"/>
|
|
6
|
+
<path d="m45 65 8-26 13 24-9-2 3 12Z" fill="#000"/>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare const listOperation: INodeProperties;
|
|
3
|
+
export declare function getAndListOperations(resource: string): INodeProperties;
|
|
4
|
+
export declare function idLocator(resource: string, displayName: string, name: string, searchListMethod: string, operations?: string[]): INodeProperties;
|
|
5
|
+
export declare function companyFilter(resource: string): INodeProperties;
|
|
6
|
+
export declare function paginationFields(resource: string): INodeProperties[];
|
|
7
|
+
export declare const dateRangeFields: (resource: string) => INodeProperties[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dateRangeFields = exports.listOperation = void 0;
|
|
4
|
+
exports.getAndListOperations = getAndListOperations;
|
|
5
|
+
exports.idLocator = idLocator;
|
|
6
|
+
exports.companyFilter = companyFilter;
|
|
7
|
+
exports.paginationFields = paginationFields;
|
|
8
|
+
exports.listOperation = {
|
|
9
|
+
displayName: 'Operation',
|
|
10
|
+
name: 'operation',
|
|
11
|
+
type: 'options',
|
|
12
|
+
noDataExpression: true,
|
|
13
|
+
options: [{ name: 'Get Many', value: 'getMany', action: 'Get many records' }],
|
|
14
|
+
default: 'getMany',
|
|
15
|
+
};
|
|
16
|
+
function getAndListOperations(resource) {
|
|
17
|
+
return {
|
|
18
|
+
displayName: 'Operation',
|
|
19
|
+
name: 'operation',
|
|
20
|
+
type: 'options',
|
|
21
|
+
noDataExpression: true,
|
|
22
|
+
displayOptions: { show: { resource: [resource] } },
|
|
23
|
+
options: [
|
|
24
|
+
{ name: 'Get', value: 'get', action: `Get a ${resource}` },
|
|
25
|
+
{ name: 'Get Many', value: 'getMany', action: `Get many ${resource}s` },
|
|
26
|
+
],
|
|
27
|
+
default: 'getMany',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function idLocator(resource, displayName, name, searchListMethod, operations = ['get']) {
|
|
31
|
+
return {
|
|
32
|
+
displayName,
|
|
33
|
+
name,
|
|
34
|
+
type: 'resourceLocator',
|
|
35
|
+
default: { mode: 'list', value: '' },
|
|
36
|
+
required: true,
|
|
37
|
+
displayOptions: { show: { resource: [resource], operation: operations } },
|
|
38
|
+
description: 'Select from the list or enter a UUID. Searching scans all API pages because AutoElevate does not expose a server-side search parameter.',
|
|
39
|
+
modes: [
|
|
40
|
+
{
|
|
41
|
+
displayName: 'From List',
|
|
42
|
+
name: 'list',
|
|
43
|
+
type: 'list',
|
|
44
|
+
typeOptions: { searchListMethod, searchable: true },
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
displayName: 'By ID',
|
|
48
|
+
name: 'id',
|
|
49
|
+
type: 'string',
|
|
50
|
+
placeholder: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
51
|
+
validation: [
|
|
52
|
+
{
|
|
53
|
+
type: 'regex',
|
|
54
|
+
properties: {
|
|
55
|
+
regex: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$',
|
|
56
|
+
errorMessage: 'Enter a valid UUID',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function companyFilter(resource) {
|
|
65
|
+
return {
|
|
66
|
+
displayName: 'Company',
|
|
67
|
+
name: 'companyId',
|
|
68
|
+
type: 'resourceLocator',
|
|
69
|
+
default: { mode: 'list', value: '' },
|
|
70
|
+
displayOptions: { show: { resource: [resource], operation: ['getMany'] } },
|
|
71
|
+
description: 'Optionally scope results to one managed company',
|
|
72
|
+
modes: [
|
|
73
|
+
{
|
|
74
|
+
displayName: 'From List',
|
|
75
|
+
name: 'list',
|
|
76
|
+
type: 'list',
|
|
77
|
+
typeOptions: { searchListMethod: 'searchCompanies', searchable: true },
|
|
78
|
+
},
|
|
79
|
+
{ displayName: 'By ID', name: 'id', type: 'string' },
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function paginationFields(resource) {
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
displayName: 'Return All',
|
|
87
|
+
name: 'returnAll',
|
|
88
|
+
type: 'boolean',
|
|
89
|
+
default: false,
|
|
90
|
+
displayOptions: { show: { resource: [resource], operation: ['getMany'] } },
|
|
91
|
+
description: 'Whether to return all results or only up to a given limit',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
displayName: 'Limit',
|
|
95
|
+
name: 'limit',
|
|
96
|
+
type: 'number',
|
|
97
|
+
default: 50,
|
|
98
|
+
typeOptions: { minValue: 1, maxValue: 10000 },
|
|
99
|
+
displayOptions: {
|
|
100
|
+
show: { resource: [resource], operation: ['getMany'], returnAll: [false] },
|
|
101
|
+
},
|
|
102
|
+
description: 'Max number of results to return',
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
const dateRangeFields = (resource) => [
|
|
107
|
+
{
|
|
108
|
+
displayName: 'Start',
|
|
109
|
+
name: 'start',
|
|
110
|
+
type: 'dateTime',
|
|
111
|
+
default: '',
|
|
112
|
+
displayOptions: { show: { resource: [resource], operation: ['getMany'] } },
|
|
113
|
+
description: 'Inclusive start of the date range',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
displayName: 'End',
|
|
117
|
+
name: 'end',
|
|
118
|
+
type: 'dateTime',
|
|
119
|
+
default: '',
|
|
120
|
+
displayOptions: { show: { resource: [resource], operation: ['getMany'] } },
|
|
121
|
+
description: 'Inclusive end of the date range',
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
exports.dateRangeFields = dateRangeFields;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.autoElevateProperties = void 0;
|
|
4
|
+
const Common_1 = require("./Common");
|
|
5
|
+
const getManyOnly = (resource, action) => ({
|
|
6
|
+
displayName: 'Operation',
|
|
7
|
+
name: 'operation',
|
|
8
|
+
type: 'options',
|
|
9
|
+
noDataExpression: true,
|
|
10
|
+
displayOptions: { show: { resource: [resource] } },
|
|
11
|
+
options: [{ name: 'Get Many', value: 'getMany', action }],
|
|
12
|
+
default: 'getMany',
|
|
13
|
+
});
|
|
14
|
+
exports.autoElevateProperties = [
|
|
15
|
+
getManyOnly('auditLog', 'Get many audit log entries'),
|
|
16
|
+
...(0, Common_1.dateRangeFields)('auditLog'),
|
|
17
|
+
{
|
|
18
|
+
displayName: 'Entity Type',
|
|
19
|
+
name: 'entityType',
|
|
20
|
+
type: 'options',
|
|
21
|
+
default: '',
|
|
22
|
+
options: [
|
|
23
|
+
{ name: 'All', value: '' },
|
|
24
|
+
{ name: 'Settings', value: 'settings' },
|
|
25
|
+
{ name: 'Users', value: 'users' },
|
|
26
|
+
],
|
|
27
|
+
displayOptions: { show: { resource: ['auditLog'], operation: ['getMany'] } },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Action',
|
|
31
|
+
name: 'auditAction',
|
|
32
|
+
type: 'options',
|
|
33
|
+
default: '',
|
|
34
|
+
options: [
|
|
35
|
+
{ name: 'All', value: '' },
|
|
36
|
+
{ name: 'Created', value: 'created' },
|
|
37
|
+
{ name: 'Updated', value: 'updated' },
|
|
38
|
+
{ name: 'Deleted', value: 'deleted' },
|
|
39
|
+
],
|
|
40
|
+
displayOptions: { show: { resource: ['auditLog'], operation: ['getMany'] } },
|
|
41
|
+
},
|
|
42
|
+
...(0, Common_1.paginationFields)('auditLog'),
|
|
43
|
+
(0, Common_1.getAndListOperations)('company'),
|
|
44
|
+
(0, Common_1.idLocator)('company', 'Company', 'companyId', 'searchCompanies'),
|
|
45
|
+
...(0, Common_1.paginationFields)('company'),
|
|
46
|
+
(0, Common_1.getAndListOperations)('computer'),
|
|
47
|
+
(0, Common_1.idLocator)('computer', 'Computer', 'computerId', 'searchComputers'),
|
|
48
|
+
(0, Common_1.companyFilter)('computer'),
|
|
49
|
+
{
|
|
50
|
+
displayName: 'Location',
|
|
51
|
+
name: 'locationId',
|
|
52
|
+
type: 'resourceLocator',
|
|
53
|
+
default: { mode: 'list', value: '' },
|
|
54
|
+
displayOptions: { show: { resource: ['computer'], operation: ['getMany'] } },
|
|
55
|
+
modes: [
|
|
56
|
+
{
|
|
57
|
+
displayName: 'From List',
|
|
58
|
+
name: 'list',
|
|
59
|
+
type: 'list',
|
|
60
|
+
typeOptions: { searchListMethod: 'searchLocations', searchable: true },
|
|
61
|
+
},
|
|
62
|
+
{ displayName: 'By ID', name: 'id', type: 'string' },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
...(0, Common_1.paginationFields)('computer'),
|
|
66
|
+
(0, Common_1.getAndListOperations)('elevatedSession'),
|
|
67
|
+
(0, Common_1.idLocator)('elevatedSession', 'Elevated Session', 'elevatedSessionId', 'searchElevatedSessions'),
|
|
68
|
+
(0, Common_1.companyFilter)('elevatedSession'),
|
|
69
|
+
{
|
|
70
|
+
displayName: 'Computer',
|
|
71
|
+
name: 'computerId',
|
|
72
|
+
type: 'resourceLocator',
|
|
73
|
+
default: { mode: 'list', value: '' },
|
|
74
|
+
displayOptions: { show: { resource: ['elevatedSession'], operation: ['getMany'] } },
|
|
75
|
+
modes: [
|
|
76
|
+
{
|
|
77
|
+
displayName: 'From List',
|
|
78
|
+
name: 'list',
|
|
79
|
+
type: 'list',
|
|
80
|
+
typeOptions: { searchListMethod: 'searchComputers', searchable: true },
|
|
81
|
+
},
|
|
82
|
+
{ displayName: 'By ID', name: 'id', type: 'string' },
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
displayName: 'Status',
|
|
87
|
+
name: 'status',
|
|
88
|
+
type: 'options',
|
|
89
|
+
default: '',
|
|
90
|
+
options: [
|
|
91
|
+
{ name: 'All', value: '' },
|
|
92
|
+
{ name: 'Active', value: 'active' },
|
|
93
|
+
{ name: 'Inactive', value: 'inactive' },
|
|
94
|
+
],
|
|
95
|
+
displayOptions: { show: { resource: ['elevatedSession'], operation: ['getMany'] } },
|
|
96
|
+
},
|
|
97
|
+
...(0, Common_1.paginationFields)('elevatedSession'),
|
|
98
|
+
getManyOnly('elevationEvent', 'Get many elevation events'),
|
|
99
|
+
...(0, Common_1.dateRangeFields)('elevationEvent'),
|
|
100
|
+
(0, Common_1.companyFilter)('elevationEvent'),
|
|
101
|
+
...(0, Common_1.paginationFields)('elevationEvent'),
|
|
102
|
+
{
|
|
103
|
+
displayName: 'Operation',
|
|
104
|
+
name: 'operation',
|
|
105
|
+
type: 'options',
|
|
106
|
+
noDataExpression: true,
|
|
107
|
+
displayOptions: { show: { resource: ['elevationRequest'] } },
|
|
108
|
+
options: [
|
|
109
|
+
{ name: 'Approve', value: 'approve', action: 'Approve an elevation request' },
|
|
110
|
+
{ name: 'Deny', value: 'deny', action: 'Deny an elevation request' },
|
|
111
|
+
{ name: 'Get', value: 'get', action: 'Get an elevation request' },
|
|
112
|
+
{ name: 'Get Many', value: 'getMany', action: 'Get many elevation requests' },
|
|
113
|
+
],
|
|
114
|
+
default: 'getMany',
|
|
115
|
+
},
|
|
116
|
+
(0, Common_1.idLocator)('elevationRequest', 'Elevation Request', 'elevationRequestId', 'searchElevationRequests', ['get', 'approve', 'deny']),
|
|
117
|
+
...(0, Common_1.dateRangeFields)('elevationRequest'),
|
|
118
|
+
(0, Common_1.companyFilter)('elevationRequest'),
|
|
119
|
+
{
|
|
120
|
+
displayName: 'Approval State',
|
|
121
|
+
name: 'approvalState',
|
|
122
|
+
type: 'options',
|
|
123
|
+
default: '',
|
|
124
|
+
options: [
|
|
125
|
+
{ name: 'All', value: '' },
|
|
126
|
+
{ name: 'Approved', value: 'APPROVED' },
|
|
127
|
+
{ name: 'Denied', value: 'DENIED' },
|
|
128
|
+
{ name: 'Pending', value: 'PENDING' },
|
|
129
|
+
],
|
|
130
|
+
displayOptions: { show: { resource: ['elevationRequest'], operation: ['getMany'] } },
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
displayName: 'Elevation Type',
|
|
134
|
+
name: 'elevationType',
|
|
135
|
+
type: 'options',
|
|
136
|
+
default: '',
|
|
137
|
+
options: [
|
|
138
|
+
{ name: 'Use Request Default', value: '' },
|
|
139
|
+
{ name: 'Admin', value: 'admin' },
|
|
140
|
+
{ name: 'User', value: 'user' },
|
|
141
|
+
],
|
|
142
|
+
displayOptions: { show: { resource: ['elevationRequest'], operation: ['approve'] } },
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
displayName: 'Duration (Minutes)',
|
|
146
|
+
name: 'durationInMinutes',
|
|
147
|
+
type: 'number',
|
|
148
|
+
default: 0,
|
|
149
|
+
typeOptions: { minValue: 0 },
|
|
150
|
+
description: 'For session requests. Leave at 0 to use the request default.',
|
|
151
|
+
displayOptions: { show: { resource: ['elevationRequest'], operation: ['approve'] } },
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
displayName: 'Create Rule',
|
|
155
|
+
name: 'createRule',
|
|
156
|
+
type: 'boolean',
|
|
157
|
+
default: false,
|
|
158
|
+
description: 'Whether to create a matching auto-approval or auto-denial rule',
|
|
159
|
+
displayOptions: {
|
|
160
|
+
show: { resource: ['elevationRequest'], operation: ['approve', 'deny'] },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
displayName: 'Rule Level',
|
|
165
|
+
name: 'ruleLevel',
|
|
166
|
+
type: 'options',
|
|
167
|
+
default: 'computer',
|
|
168
|
+
required: true,
|
|
169
|
+
options: [
|
|
170
|
+
{ name: 'Company', value: 'company' },
|
|
171
|
+
{ name: 'Computer', value: 'computer' },
|
|
172
|
+
{ name: 'Location', value: 'location' },
|
|
173
|
+
{ name: 'MSP', value: 'msp' },
|
|
174
|
+
],
|
|
175
|
+
displayOptions: {
|
|
176
|
+
show: {
|
|
177
|
+
resource: ['elevationRequest'],
|
|
178
|
+
operation: ['approve', 'deny'],
|
|
179
|
+
createRule: [true],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
...(0, Common_1.paginationFields)('elevationRequest'),
|
|
184
|
+
getManyOnly('elevationRule', 'Get many elevation rules'),
|
|
185
|
+
(0, Common_1.companyFilter)('elevationRule'),
|
|
186
|
+
...(0, Common_1.paginationFields)('elevationRule'),
|
|
187
|
+
(0, Common_1.getAndListOperations)('location'),
|
|
188
|
+
(0, Common_1.idLocator)('location', 'Location', 'locationId', 'searchLocations'),
|
|
189
|
+
(0, Common_1.companyFilter)('location'),
|
|
190
|
+
...(0, Common_1.paginationFields)('location'),
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Operation',
|
|
193
|
+
name: 'operation',
|
|
194
|
+
type: 'options',
|
|
195
|
+
noDataExpression: true,
|
|
196
|
+
displayOptions: { show: { resource: ['usage'] } },
|
|
197
|
+
options: [{ name: 'Get', value: 'get', action: 'Get partner usage data' }],
|
|
198
|
+
default: 'get',
|
|
199
|
+
},
|
|
200
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@joshuanode/n8n-nodes-autoelevate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "n8n community node for the AutoElevate Partner API",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Joshua Smith",
|
|
8
|
+
"email": "ajoshuasmith@users.noreply.github.com"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/ajoshuasmith/n8n-nodes-autoelevate.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/ajoshuasmith/n8n-nodes-autoelevate#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/ajoshuasmith/n8n-nodes-autoelevate/issues"
|
|
17
|
+
},
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"files": ["dist"],
|
|
21
|
+
"publishConfig": { "access": "public" },
|
|
22
|
+
"engines": { "node": ">=18.17.0" },
|
|
23
|
+
"keywords": [
|
|
24
|
+
"n8n-community-node-package",
|
|
25
|
+
"n8n",
|
|
26
|
+
"autoelevate",
|
|
27
|
+
"privileged-access-management",
|
|
28
|
+
"endpoint-security",
|
|
29
|
+
"msp"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "n8n-node build",
|
|
33
|
+
"dev": "n8n-node dev",
|
|
34
|
+
"lint": "n8n-node lint",
|
|
35
|
+
"lint:fix": "n8n-node lint --fix",
|
|
36
|
+
"release": "n8n-node release",
|
|
37
|
+
"test": "npm run build --silent && node --test test/*.test.mjs"
|
|
38
|
+
},
|
|
39
|
+
"n8n": {
|
|
40
|
+
"n8nNodesApiVersion": 1,
|
|
41
|
+
"strict": true,
|
|
42
|
+
"credentials": ["dist/credentials/AutoElevateApi.credentials.js"],
|
|
43
|
+
"nodes": ["dist/nodes/AutoElevate/AutoElevate.node.js"]
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@n8n/node-cli": "0.40.3",
|
|
47
|
+
"@types/node": "^24.1.0",
|
|
48
|
+
"n8n-workflow": "*",
|
|
49
|
+
"typescript": "^5.9.2"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": { "n8n-workflow": "*" }
|
|
52
|
+
}
|