@hahnpro/hpc-api 3.4.2 → 3.5.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/dist/api.d.ts CHANGED
@@ -15,6 +15,7 @@ import { FlowFunctionService } from './flow-function.service';
15
15
  import { FlowModuleService } from './flow-module.service';
16
16
  import { FlowDeploymentService } from './flow-deployment.service';
17
17
  import { LabelService } from './label.service';
18
+ import { VaultService } from './vault.service';
18
19
  export declare class API {
19
20
  httpClient: HttpClient;
20
21
  assets: AssetService;
@@ -32,6 +33,7 @@ export declare class API {
32
33
  tasks: TaskService;
33
34
  timeSeries: TimeSeriesService;
34
35
  users: UserService;
36
+ vault: VaultService;
35
37
  /**
36
38
  * @deprecated use "assets" instead
37
39
  */
package/dist/api.js CHANGED
@@ -18,6 +18,7 @@ const flow_function_service_1 = require("./flow-function.service");
18
18
  const flow_module_service_1 = require("./flow-module.service");
19
19
  const flow_deployment_service_1 = require("./flow-deployment.service");
20
20
  const label_service_1 = require("./label.service");
21
+ const vault_service_1 = require("./vault.service");
21
22
  class API {
22
23
  constructor() {
23
24
  const normalizePath = (value = '', defaultValue = '') => value.replace(/(?:^\/+)|(?:\/+$)/g, '') || defaultValue;
@@ -51,6 +52,7 @@ class API {
51
52
  this.tasks = new task_service_1.TaskService(this.httpClient);
52
53
  this.timeSeries = new timeseries_service_1.TimeSeriesService(this.httpClient);
53
54
  this.users = new user_service_1.UserService(this.httpClient);
55
+ this.vault = new vault_service_1.VaultService(this.httpClient);
54
56
  this.assetManager = this.assets;
55
57
  this.contentManager = this.contents;
56
58
  this.endpointManager = this.endpoints;
@@ -27,7 +27,7 @@ export declare class FlowDeploymentService extends BaseService {
27
27
  readWritePermissions?: string[];
28
28
  tags?: string[];
29
29
  }): Promise<FlowDeployment>;
30
- subscribeToStatus(id: string, listener: (event: MessageEvent<any>) => void, errorListener?: (event: MessageEvent) => void): Promise<string>;
31
- subscribeToLogs(id: string, listener: (event: MessageEvent<any>) => void, errorListener?: (event: MessageEvent) => void): Promise<string>;
30
+ subscribeToStatus(id: string, listener: (event: MessageEvent<any>) => void, errorListener?: (event: MessageEvent) => void): Promise<any>;
31
+ subscribeToLogs(id: string, listener: (event: MessageEvent<any>) => void, errorListener?: (event: MessageEvent) => void): Promise<any>;
32
32
  }
33
33
  export {};
@@ -7,8 +7,8 @@ export declare class HttpClient {
7
7
  private readonly clientId;
8
8
  private readonly clientSecret;
9
9
  private readonly axiosInstance;
10
+ private readonly authAxiosInstance;
10
11
  private readonly requestQueue;
11
- private client;
12
12
  private tokenSet;
13
13
  eventSourcesMap: Map<string, {
14
14
  eventSource: EventSource;
@@ -27,8 +27,11 @@ export declare class HttpClient {
27
27
  post: <T>(url: string, data: any, config?: AxiosRequestConfig) => Promise<T>;
28
28
  put: <T>(url: string, data: any, config?: AxiosRequestConfig) => Promise<T>;
29
29
  private request;
30
- addEventSource(url: string, listener: (event: MessageEvent) => void, errorListener?: (event: MessageEvent) => void): Promise<string>;
30
+ addEventSource(url: string, listener: (event: MessageEvent) => void, errorListener?: (event: MessageEvent) => void): Promise<any>;
31
31
  destroyEventSource(id: string): void;
32
32
  destroyAllEventSources(): void;
33
33
  getAccessToken: () => Promise<string>;
34
+ private validateIssuer;
35
+ private discoverIssuer;
36
+ private requestAccessToken;
34
37
  }
@@ -4,9 +4,11 @@ exports.HttpClient = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const axios_1 = tslib_1.__importDefault(require("axios"));
6
6
  const eventsource_1 = tslib_1.__importDefault(require("eventsource"));
7
- const openid_client_1 = require("openid-client");
7
+ const uuid_1 = require("uuid");
8
+ const jose_1 = require("jose");
8
9
  const Queue_1 = require("./Queue");
9
- const crypto_1 = require("crypto");
10
+ const token_set_1 = require("./token-set");
11
+ const querystring_1 = require("querystring");
10
12
  const TOKEN_EXPIRATION_BUFFER = 30;
11
13
  class HttpClient {
12
14
  constructor(baseURL, authBaseURL, realm, clientId, clientSecret) {
@@ -33,25 +35,17 @@ class HttpClient {
33
35
  }));
34
36
  };
35
37
  this.getAccessToken = async () => {
36
- var _a;
37
- if (!((_a = this.client) === null || _a === void 0 ? void 0 : _a.issuer)) {
38
- const authIssuer = await openid_client_1.Issuer.discover(`${this.authBaseURL}/realms/${this.realm}/`);
39
- this.client = await new authIssuer.Client({
40
- client_id: this.clientId,
41
- client_secret: this.clientSecret,
42
- token_endpoint_auth_method: 'client_secret_jwt',
43
- });
44
- }
45
- if (!this.tokenSet || this.tokenSet.expired() || this.tokenSet.expires_at < Date.now() / 1000 + TOKEN_EXPIRATION_BUFFER) {
46
- this.tokenSet = await this.client.grant({ grant_type: 'client_credentials' });
38
+ if (!this.tokenSet || this.tokenSet.expired() || this.tokenSet.expiresAt < Date.now() / 1000 + TOKEN_EXPIRATION_BUFFER) {
39
+ return this.requestAccessToken();
47
40
  }
48
41
  return this.tokenSet.access_token;
49
42
  };
50
43
  this.axiosInstance = axios_1.default.create({ baseURL, timeout: 60000 });
44
+ this.authAxiosInstance = axios_1.default.create({ baseURL: authBaseURL || baseURL, timeout: 10000 });
51
45
  this.requestQueue = new Queue_1.Queue({ concurrency: 1, timeout: 70000, throwOnTimeout: true });
52
46
  }
53
47
  async addEventSource(url, listener, errorListener) {
54
- const id = (0, crypto_1.randomUUID)();
48
+ const id = (0, uuid_1.v4)();
55
49
  const errListener = errorListener
56
50
  ? errorListener
57
51
  : (event) => {
@@ -80,5 +74,60 @@ class HttpClient {
80
74
  this.destroyEventSource(key);
81
75
  }
82
76
  }
77
+ validateIssuer(issuer) {
78
+ var _a, _b;
79
+ if (!issuer.issuer ||
80
+ !((_a = issuer.grant_types_supported) === null || _a === void 0 ? void 0 : _a.includes('client_credentials')) ||
81
+ !((_b = issuer.token_endpoint_auth_methods_supported) === null || _b === void 0 ? void 0 : _b.includes('client_secret_jwt'))) {
82
+ throw new Error('Issuer does not support client_secret_jwt');
83
+ }
84
+ return issuer;
85
+ }
86
+ async discoverIssuer(uri) {
87
+ const wellKnownUri = `${uri}/.well-known/openid-configuration`;
88
+ const issuerResponse = await this.authAxiosInstance.get(wellKnownUri, {
89
+ responseType: 'json',
90
+ headers: { Accept: 'application/json' },
91
+ });
92
+ return this.validateIssuer(issuerResponse.data);
93
+ }
94
+ async requestAccessToken() {
95
+ var _a, _b;
96
+ const issuer = await this.discoverIssuer(`${this.authBaseURL}/realms/${this.realm}`);
97
+ const timestamp = Date.now() / 1000;
98
+ const audience = [...new Set([issuer.issuer, issuer.token_endpoint].filter(Boolean))];
99
+ const assertionPayload = {
100
+ iat: timestamp,
101
+ exp: timestamp + 60,
102
+ jti: (0, uuid_1.v4)(),
103
+ iss: this.clientId,
104
+ sub: this.clientId,
105
+ aud: audience,
106
+ };
107
+ const supportedAlgos = issuer.token_endpoint_auth_signing_alg_values_supported;
108
+ const alg = (_a = issuer.token_endpoint_auth_signing_alg) !== null && _a !== void 0 ? _a : (Array.isArray(supportedAlgos) && supportedAlgos.find((signAlg) => /^HS(?:256|384|512)/.test(signAlg)));
109
+ if (!alg) {
110
+ throw new Error('Issuer has to support HS256, HS384 or HS512');
111
+ }
112
+ const assertion = await new jose_1.CompactSign(Buffer.from(JSON.stringify(assertionPayload)))
113
+ .setProtectedHeader({ alg })
114
+ .sign(new TextEncoder().encode(this.clientSecret));
115
+ const opts = {
116
+ client_id: this.clientId,
117
+ client_assertion: assertion,
118
+ client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
119
+ grant_type: 'client_credentials',
120
+ };
121
+ const authResponse = await this.authAxiosInstance.post(issuer.token_endpoint, (0, querystring_1.stringify)(opts), {
122
+ headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded' },
123
+ });
124
+ if (((_b = authResponse === null || authResponse === void 0 ? void 0 : authResponse.data) === null || _b === void 0 ? void 0 : _b.access_token) && authResponse.data.expires_in) {
125
+ this.tokenSet = new token_set_1.TokenSet(authResponse.data.access_token, authResponse.data.expires_in);
126
+ return authResponse.data.access_token;
127
+ }
128
+ else {
129
+ throw new Error('Invalid access token received');
130
+ }
131
+ }
83
132
  }
84
133
  exports.HttpClient = HttpClient;
package/dist/index.d.ts CHANGED
@@ -16,4 +16,5 @@ export * from './flow-function.interface';
16
16
  export * from './flow.interface';
17
17
  export * from './resource.interface';
18
18
  export * from './schema.interface';
19
+ export * from './vault.interface';
19
20
  export * as SiDrive from './sidriveiq.interface';
package/dist/index.js CHANGED
@@ -20,4 +20,5 @@ tslib_1.__exportStar(require("./flow-function.interface"), exports);
20
20
  tslib_1.__exportStar(require("./flow.interface"), exports);
21
21
  tslib_1.__exportStar(require("./resource.interface"), exports);
22
22
  tslib_1.__exportStar(require("./schema.interface"), exports);
23
+ tslib_1.__exportStar(require("./vault.interface"), exports);
23
24
  exports.SiDrive = tslib_1.__importStar(require("./sidriveiq.interface"));
@@ -26,6 +26,8 @@ import { FlowModule } from '../flow-module.interface';
26
26
  import { Artifact } from '../storage.interface';
27
27
  import { LabelMockService } from './label.mock.service';
28
28
  import { Label } from '../label.interface';
29
+ import { VaultSecret } from '../vault.interface';
30
+ import { VaultMockService } from './vault.mock.service';
29
31
  export declare class MockAPI implements API {
30
32
  httpClient: any;
31
33
  assets: AssetMockService;
@@ -43,6 +45,7 @@ export declare class MockAPI implements API {
43
45
  tasks: TaskMockService;
44
46
  timeSeries: TimeseriesMockService;
45
47
  users: UserMockService;
48
+ vault: VaultMockService;
46
49
  assetManager: AssetMockService;
47
50
  contentManager: ContentMockService;
48
51
  endpointManager: EndpointMockService;
@@ -70,6 +73,7 @@ export declare class MockAPI implements API {
70
73
  modules?: FlowModuleInit[];
71
74
  diagrams?: FlowDiagramInit[];
72
75
  labels?: LabelInit[];
76
+ vault?: VaultSecretInit[];
73
77
  });
74
78
  }
75
79
  export declare type Identity<T> = {
@@ -104,6 +108,7 @@ export declare type ArtifactInit = AtLeast<Artifact & {
104
108
  }, 'filename' | 'path'>;
105
109
  export declare type FlowDiagramInit = AtLeast<FlowDiagram, 'id' | 'flow'>;
106
110
  export declare type LabelInit = AtLeast<Label, 'id' | 'name'>;
111
+ export declare type VaultSecretInit = AtLeast<VaultSecret, 'name' | 'secret'>;
107
112
  export interface UserInit {
108
113
  roles: string[];
109
114
  }
@@ -17,12 +17,13 @@ const flow_functions_mock_service_1 = require("./flow-functions.mock.service");
17
17
  const flow_modules_mock_service_1 = require("./flow-modules.mock.service");
18
18
  const crypto_1 = require("crypto");
19
19
  const label_mock_service_1 = require("./label.mock.service");
20
+ const vault_mock_service_1 = require("./vault.mock.service");
20
21
  class MockAPI {
21
22
  constructor(initData) {
22
23
  this.httpClient = null;
23
24
  this.proxy = null;
24
25
  this.siDrive = null;
25
- const { assets = [], assetRevisions = [], contents = [], endpoints = [], secrets = [], timeSeries = [], tasks = [], events = [], users, flows = [], flowRevisions = [], deployments = [], functions = [], functionRevisions = [], modules = [], diagrams = [], labels = [], } = initData;
26
+ const { assets = [], assetRevisions = [], contents = [], endpoints = [], secrets = [], timeSeries = [], tasks = [], events = [], users, flows = [], flowRevisions = [], deployments = [], functions = [], functionRevisions = [], modules = [], diagrams = [], labels = [], vault = [], } = initData;
26
27
  const assetTypes = assets
27
28
  .map((v) => v.type)
28
29
  .map((v) => {
@@ -111,6 +112,7 @@ class MockAPI {
111
112
  return (Object.assign(Object.assign({}, v), { artifacts: (_a = modules[index].artifacts.map((art) => (Object.assign(Object.assign({}, art), { version: '0.0.0', id: (0, crypto_1.randomUUID)(), mimetype: '', size: 0, createdAt: '' + Date.now() })))) !== null && _a !== void 0 ? _a : [], author: 'nobody', functions: [], readPermissions: [], readWritePermissions: [] }));
112
113
  });
113
114
  const labels1 = labels.map((label) => (Object.assign(Object.assign({}, label), { color: '', description: '', readPermissions: [], readWritePermissions: [] })));
115
+ const vaultSecrets1 = vault.map((v) => (Object.assign(Object.assign({}, v), { readPermissions: [], readWritePermissions: [] })));
114
116
  this.assets = new asset_mock_service_1.AssetMockService(this, assets1, assetRevisions1);
115
117
  this.contents = new content_mock_service_1.ContentMockService(contents1, contentData);
116
118
  this.endpoints = new endpoint_mock_service_1.EndpointMockService(endpoint1);
@@ -124,6 +126,7 @@ class MockAPI {
124
126
  this.flowFunctions = new flow_functions_mock_service_1.FlowFunctionsMockService(functions1, functionRevisions1);
125
127
  this.flowModules = new flow_modules_mock_service_1.FlowModulesMockService(modules1);
126
128
  this.labels = new label_mock_service_1.LabelMockService(labels1);
129
+ this.vault = new vault_mock_service_1.VaultMockService(vaultSecrets1);
127
130
  this.assetManager = this.assets;
128
131
  this.contentManager = this.contents;
129
132
  this.endpointManager = this.endpoints;
@@ -9,6 +9,8 @@ export declare class DataMockService<T> extends DataService<T> implements APIBas
9
9
  deleteOne(id: string): Promise<any>;
10
10
  getMany(params?: RequestParameter): Promise<Paginated<T[]>>;
11
11
  getManyFiltered(filter: Filter, params?: RequestParameter): Promise<Paginated<T[]>>;
12
- getOne(id: string, options?: any): Promise<T>;
12
+ getOne(id: string, options?: Record<string, any> & {
13
+ idKey?: string;
14
+ }): Promise<T>;
13
15
  updateOne(id: string, dto: any): Promise<T>;
14
16
  }
@@ -40,8 +40,9 @@ class DataMockService extends data_service_1.DataService {
40
40
  };
41
41
  return Promise.resolve(page);
42
42
  }
43
- getOne(id, options) {
44
- const t = this.data.find((v) => v.id === id);
43
+ getOne(id, options = {}) {
44
+ const idKey = options.idKey || 'id';
45
+ const t = this.data.find((v) => v[idKey] === id);
45
46
  return Promise.resolve(t);
46
47
  }
47
48
  async updateOne(id, dto) {
@@ -7,3 +7,4 @@ export * from './secret.mock.service';
7
7
  export * from './timeseries.mock.service';
8
8
  export * from './task.mock.service';
9
9
  export * from './events.mock.service';
10
+ export * from './vault.mock.service';
@@ -10,3 +10,4 @@ tslib_1.__exportStar(require("./secret.mock.service"), exports);
10
10
  tslib_1.__exportStar(require("./timeseries.mock.service"), exports);
11
11
  tslib_1.__exportStar(require("./task.mock.service"), exports);
12
12
  tslib_1.__exportStar(require("./events.mock.service"), exports);
13
+ tslib_1.__exportStar(require("./vault.mock.service"), exports);
@@ -0,0 +1,7 @@
1
+ import { DataMockService } from './data.mock.service';
2
+ import { VaultSecret } from '../vault.interface';
3
+ import { VaultService } from '../vault.service';
4
+ export declare class VaultMockService extends DataMockService<VaultSecret> implements VaultService {
5
+ constructor(secrets: VaultSecret[]);
6
+ getSecret(name: string, version?: number): Promise<string>;
7
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VaultMockService = void 0;
4
+ const data_mock_service_1 = require("./data.mock.service");
5
+ class VaultMockService extends data_mock_service_1.DataMockService {
6
+ constructor(secrets) {
7
+ super();
8
+ this.data = secrets;
9
+ }
10
+ getSecret(name, version) {
11
+ const vaultSecret = this.data.find((v) => v.name === name);
12
+ if (version && vaultSecret.version !== version) {
13
+ return Promise.resolve('');
14
+ }
15
+ return Promise.resolve(vaultSecret.secret);
16
+ }
17
+ }
18
+ exports.VaultMockService = VaultMockService;
@@ -0,0 +1,8 @@
1
+ export declare class TokenSet {
2
+ expiresAt: number;
3
+ access_token: string;
4
+ constructor(access_token: string, expires_in: number);
5
+ set expires_in(value: number);
6
+ get expires_in(): number;
7
+ expired(): boolean;
8
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TokenSet = void 0;
4
+ class TokenSet {
5
+ constructor(access_token, expires_in) {
6
+ this.expires_in = expires_in;
7
+ this.access_token = access_token;
8
+ }
9
+ set expires_in(value) {
10
+ this.expiresAt = Date.now() + Number(value);
11
+ }
12
+ get expires_in() {
13
+ return Math.max(this.expiresAt - Date.now(), 0);
14
+ }
15
+ expired() {
16
+ return this.expires_in === 0;
17
+ }
18
+ }
19
+ exports.TokenSet = TokenSet;
@@ -0,0 +1,10 @@
1
+ export interface VaultSecret {
2
+ id?: string;
3
+ name: string;
4
+ secret?: string;
5
+ tags?: string[];
6
+ version?: number;
7
+ readPermissions: string[];
8
+ readWritePermissions: string[];
9
+ updatedAt?: string;
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ import { DataService } from './data.service';
2
+ import { VaultSecret } from './vault.interface';
3
+ import { HttpClient } from './http.service';
4
+ export declare class VaultService extends DataService<VaultSecret> {
5
+ constructor(httpClient: HttpClient);
6
+ getSecret(name: string, version?: number): Promise<string>;
7
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VaultService = void 0;
4
+ const data_service_1 = require("./data.service");
5
+ class VaultService extends data_service_1.DataService {
6
+ constructor(httpClient) {
7
+ super(httpClient, '/vault');
8
+ }
9
+ getSecret(name, version) {
10
+ const params = version ? { version } : {};
11
+ return this.httpClient.get(`${this.basePath}/${name}/secret`, { params });
12
+ }
13
+ }
14
+ exports.VaultService = VaultService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hahnpro/hpc-api",
3
- "version": "3.4.2",
3
+ "version": "3.5.0",
4
4
  "description": "Module for easy access to the HahnPRO API",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -27,15 +27,15 @@
27
27
  "axios": "~0.27.2",
28
28
  "eventsource": "^2.0.2",
29
29
  "form-data": "^4.0.0",
30
+ "jose": "^4.9.0",
30
31
  "jwt-decode": "^3.1.2",
31
- "openid-client": "^5.1.8",
32
32
  "p-queue": "^6.6.2",
33
- "ts-mixer": "^6.0.1"
33
+ "ts-mixer": "^6.0.1",
34
+ "uuid": "^8.3.2"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/eventsource": "^1.1.9",
37
- "axios-mock-adapter": "^1.21.2",
38
- "nock": "^13.2.9"
38
+ "axios-mock-adapter": "^1.21.2"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=v14.13"