@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 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
+ ![AutoElevate](nodes/AutoElevate/autoElevate.png)
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@joshuanode/n8n-nodes-autoelevate.svg)](https://www.npmjs.com/package/@joshuanode/n8n-nodes-autoelevate)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![n8n](https://img.shields.io/badge/n8n-community%20node-orange.svg)](https://n8n.io/)
8
+ [![GitHub](https://img.shields.io/badge/GitHub-repo-black.svg?logo=github)](https://github.com/ajoshuasmith/n8n-nodes-autoelevate)
9
+ [![Status: Beta](https://img.shields.io/badge/status-beta-orange.svg)](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;
@@ -0,0 +1,2 @@
1
+ export * from './credentials/AutoElevateApi.credentials';
2
+ export * from './nodes/AutoElevate/AutoElevate.node';
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>
@@ -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,2 @@
1
+ import type { INodeProperties } from 'n8n-workflow';
2
+ export declare const autoElevateProperties: INodeProperties[];
@@ -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
+ }