@ianua/ianus-dataverse-react-fluentui8 1.0.5

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.
@@ -0,0 +1,109 @@
1
+ import * as React from 'react';
2
+
3
+ interface IMeta {
4
+ name: string;
5
+ }
6
+ interface IEnvironmentIdentifier {
7
+ type: string;
8
+ identifier: string;
9
+ name: string;
10
+ }
11
+ interface ILicenseClaims {
12
+ jti: string;
13
+ iss: string;
14
+ aud: string;
15
+ pub: string;
16
+ prd: string;
17
+ sub: string;
18
+ env: IEnvironmentIdentifier[];
19
+ required_roles: string[];
20
+ iat: number;
21
+ nbf: number;
22
+ exp?: number;
23
+ custom: Record<string, any>;
24
+ iss_meta: IMeta;
25
+ aud_meta: IMeta;
26
+ pub_meta: IMeta;
27
+ prd_meta: IMeta;
28
+ sub_meta: IMeta;
29
+ env_meta: Record<string, IMeta>;
30
+ ver: string;
31
+ }
32
+
33
+ type DataverseLicenseValidationError = {
34
+ isValid: false;
35
+ reason: string;
36
+ isTerminalError?: boolean;
37
+ licenseId?: string;
38
+ licenseKey?: string;
39
+ };
40
+ type DataverseLicenseValidationSuccess = {
41
+ isValid: true;
42
+ claims: ILicenseClaims;
43
+ licenseId: string;
44
+ licenseKey: string;
45
+ };
46
+ type DataverseLicenseValidationResult = DataverseLicenseValidationError | DataverseLicenseValidationSuccess;
47
+
48
+ type LicenseValidationError = {
49
+ isValid: false;
50
+ reason: string;
51
+ isTerminalError?: boolean;
52
+ };
53
+ type LicenseValidationSuccess = {
54
+ isValid: true;
55
+ claims: ILicenseClaims;
56
+ };
57
+ type LicenseValidationResult = LicenseValidationError | LicenseValidationSuccess;
58
+
59
+ interface IIanusGuardProps {
60
+ publisherId: string;
61
+ productId: string;
62
+ publicKeys: string[];
63
+ environmentType: string;
64
+ environmentIdentifier: string | ComponentFramework.WebApi;
65
+ dataProvider: ComponentFramework.WebApi | ComponentFramework.PropertyTypes.DataSet;
66
+ onLicenseValidated?: (result: LicenseValidationResult) => unknown;
67
+ }
68
+ declare const isWebApi: (dataProvider: ComponentFramework.WebApi | ComponentFramework.PropertyTypes.DataSet | string) => dataProvider is ComponentFramework.WebApi;
69
+ declare const isDataset: (dataProvider: ComponentFramework.WebApi | ComponentFramework.PropertyTypes.DataSet | string) => dataProvider is ComponentFramework.PropertyTypes.DataSet;
70
+ declare const acquireLicenses: (publisherId: string, productId: string, dataProvider: ComponentFramework.WebApi | ComponentFramework.PropertyTypes.DataSet) => Promise<ComponentFramework.WebApi.Entity[]>;
71
+ declare const IanusGuard: React.FC<IIanusGuardProps>;
72
+
73
+ type Action = {
74
+ type: "setLicense";
75
+ payload: DataverseLicenseValidationResult | undefined;
76
+ } | {
77
+ type: "setLicenseDialogVisible";
78
+ payload: boolean;
79
+ };
80
+ type IanusLicenseStateProps = {
81
+ license?: DataverseLicenseValidationResult;
82
+ licenseDialogVisible?: boolean;
83
+ };
84
+ type IanusLicenseDispatch = (action: Action) => void;
85
+ declare const IanusLicenseDispatch: React.Context<IanusLicenseDispatch | undefined>;
86
+ declare const IanusLicenseStateProvider: React.FC<IanusLicenseStateProps>;
87
+ declare const useLicenseState: () => IanusLicenseStateProps;
88
+ declare const useLicenseDispatch: () => IanusLicenseDispatch;
89
+ declare const useLicenseContext: () => [IanusLicenseStateProps, IanusLicenseDispatch];
90
+
91
+ interface IIanusProviderProps extends IIanusGuardProps {
92
+ }
93
+ declare const IanusProvider: React.FC<React.PropsWithChildren<IIanusProviderProps>>;
94
+
95
+ interface ILicenseDialogProps {
96
+ publisherId: string;
97
+ productId: string;
98
+ dataProvider: ComponentFramework.WebApi | ComponentFramework.PropertyTypes.DataSet;
99
+ onSubmit: () => void;
100
+ onCancel: () => void;
101
+ }
102
+ declare const LicenseDialog: React.FC<ILicenseDialogProps>;
103
+
104
+ declare const removeCurlyBrackets: (input: string | undefined | null) => string | undefined;
105
+
106
+ declare const base64url_decode: (value: string) => ArrayBuffer;
107
+ declare const validateLicense: (publisherId: string, productId: string, environmentType: string, environmentIdentifier: string, publicKeys: string[], licenseKey: string | undefined) => Promise<LicenseValidationResult>;
108
+
109
+ export { type DataverseLicenseValidationError, type DataverseLicenseValidationResult, type DataverseLicenseValidationSuccess, type IEnvironmentIdentifier, type IIanusGuardProps, type IIanusProviderProps, type ILicenseClaims, type ILicenseDialogProps, type IMeta, IanusGuard, IanusLicenseDispatch, type IanusLicenseStateProps, IanusLicenseStateProvider, IanusProvider, LicenseDialog, type LicenseValidationError, type LicenseValidationResult, type LicenseValidationSuccess, acquireLicenses, base64url_decode, isDataset, isWebApi, removeCurlyBrackets, useLicenseContext, useLicenseDispatch, useLicenseState, validateLicense };
package/dist/index.js ADDED
@@ -0,0 +1,557 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __defProps = Object.defineProperties;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
+ var __getProtoOf = Object.getPrototypeOf;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __spreadValues = (a, b) => {
14
+ for (var prop in b || (b = {}))
15
+ if (__hasOwnProp.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ if (__getOwnPropSymbols)
18
+ for (var prop of __getOwnPropSymbols(b)) {
19
+ if (__propIsEnum.call(b, prop))
20
+ __defNormalProp(a, prop, b[prop]);
21
+ }
22
+ return a;
23
+ };
24
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
25
+ var __export = (target, all) => {
26
+ for (var name in all)
27
+ __defProp(target, name, { get: all[name], enumerable: true });
28
+ };
29
+ var __copyProps = (to, from, except, desc) => {
30
+ if (from && typeof from === "object" || typeof from === "function") {
31
+ for (let key of __getOwnPropNames(from))
32
+ if (!__hasOwnProp.call(to, key) && key !== except)
33
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
34
+ }
35
+ return to;
36
+ };
37
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
38
+ // If the importer is in node compatibility mode or this is not an ESM
39
+ // file that has been converted to a CommonJS file using a Babel-
40
+ // compatible transform (i.e. "__esModule" has not been set), then set
41
+ // "default" to the CommonJS "module.exports" for node compatibility.
42
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
43
+ mod
44
+ ));
45
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
46
+ var __async = (__this, __arguments, generator) => {
47
+ return new Promise((resolve, reject) => {
48
+ var fulfilled = (value) => {
49
+ try {
50
+ step(generator.next(value));
51
+ } catch (e) {
52
+ reject(e);
53
+ }
54
+ };
55
+ var rejected = (value) => {
56
+ try {
57
+ step(generator.throw(value));
58
+ } catch (e) {
59
+ reject(e);
60
+ }
61
+ };
62
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
63
+ step((generator = generator.apply(__this, __arguments)).next());
64
+ });
65
+ };
66
+
67
+ // src/index.ts
68
+ var src_exports = {};
69
+ __export(src_exports, {
70
+ IanusGuard: () => IanusGuard,
71
+ IanusLicenseStateProvider: () => IanusLicenseStateProvider,
72
+ IanusProvider: () => IanusProvider,
73
+ LicenseDialog: () => LicenseDialog,
74
+ acquireLicenses: () => acquireLicenses,
75
+ base64url_decode: () => base64url_decode,
76
+ isDataset: () => isDataset,
77
+ isWebApi: () => isWebApi,
78
+ removeCurlyBrackets: () => removeCurlyBrackets,
79
+ useLicenseContext: () => useLicenseContext,
80
+ useLicenseDispatch: () => useLicenseDispatch,
81
+ useLicenseState: () => useLicenseState,
82
+ validateLicense: () => validateLicense
83
+ });
84
+ module.exports = __toCommonJS(src_exports);
85
+
86
+ // src/IanusGuard.tsx
87
+ var React3 = __toESM(require("react"));
88
+ var import_MessageBar = require("@fluentui/react/lib/MessageBar");
89
+ var import_Button = require("@fluentui/react/lib/Button");
90
+ var import_Spinner = require("@fluentui/react/lib/Spinner");
91
+
92
+ // ../../../../ianus-core/LicenseValidation.ts
93
+ var str2ab = (str) => {
94
+ const buf = new ArrayBuffer(str.length);
95
+ const bufView = new Uint8Array(buf);
96
+ for (let i = 0, strLen = str.length; i < strLen; i++) {
97
+ bufView[i] = str.charCodeAt(i);
98
+ }
99
+ return buf;
100
+ };
101
+ var importRsaKey = (pem) => {
102
+ const binaryDerString = window.atob(pem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").trim());
103
+ const binaryDer = str2ab(binaryDerString);
104
+ return window.crypto.subtle.importKey(
105
+ "spki",
106
+ binaryDer,
107
+ {
108
+ name: "RSASSA-PKCS1-v1_5",
109
+ hash: "SHA-256"
110
+ },
111
+ true,
112
+ ["verify"]
113
+ );
114
+ };
115
+ var validateClaims = (publisherId, productId, environmentType, environmentIdentifier, license) => {
116
+ if (!license) {
117
+ return [false, "No license passed!"];
118
+ }
119
+ if (!license.env || !license.env.length || !license.aud || !license.iss) {
120
+ return [false, "Incomplete license!"];
121
+ }
122
+ const validIssuer = `https://www.ianusguard.com/api/public/products/${productId}`;
123
+ if (license.iss.toLowerCase() !== validIssuer.toLowerCase()) {
124
+ return [false, `Invalid license issuer: Issuer must be '${validIssuer}'`];
125
+ }
126
+ const validAudience = "ianusguard";
127
+ if (license.aud !== validAudience) {
128
+ return [false, `Invalid license audience: Audience must be '${publisherId}'`];
129
+ }
130
+ if (license.pub !== publisherId) {
131
+ return [false, `Invalid license publisher: Publisher must be '${publisherId}'`];
132
+ }
133
+ if (license.prd !== productId) {
134
+ return [false, `Invalid license product: Product must be '${productId}'`];
135
+ }
136
+ const formattedEnvironmentIdentifier = environmentIdentifier.replace("{", "").replace("}", "").toLowerCase();
137
+ if (!license.env.some((e) => e.type.toLowerCase() === environmentType && e.identifier.toLowerCase() == formattedEnvironmentIdentifier)) {
138
+ return [false, `Invalid organization: Your license is not intended for usage in environment of type '${environmentType}' and identifier '${formattedEnvironmentIdentifier}' but for '${JSON.stringify(license.env)}'`];
139
+ }
140
+ if (license.exp != null) {
141
+ if (isNaN(license.exp)) {
142
+ return [false, "Invalid license expiry: Expiry is not a number"];
143
+ }
144
+ const expiryDate = new Date(license.exp * 1e3);
145
+ if (expiryDate < /* @__PURE__ */ new Date()) {
146
+ return [false, `Invalid license expiry: License expired on '${expiryDate}'`];
147
+ }
148
+ }
149
+ return [true, ""];
150
+ };
151
+ var base64url_decode = (value) => {
152
+ const m = value.length % 4;
153
+ return Uint8Array.from(atob(
154
+ value.replace(/-/g, "+").replace(/_/g, "/").padEnd(value.length + (m === 0 ? 0 : 4 - m), "=")
155
+ ), (c) => c.charCodeAt(0)).buffer;
156
+ };
157
+ var verifySignature = (key, dataToVerify, signature) => __async(void 0, null, function* () {
158
+ return yield window.crypto.subtle.verify(
159
+ "RSASSA-PKCS1-v1_5",
160
+ key,
161
+ signature,
162
+ dataToVerify
163
+ );
164
+ });
165
+ var validateLicense = (publisherId, productId, environmentType, environmentIdentifier, publicKeys, licenseKey) => __async(void 0, null, function* () {
166
+ if (!licenseKey) {
167
+ return {
168
+ isValid: false,
169
+ isTerminalError: false,
170
+ reason: "No license key set!"
171
+ };
172
+ }
173
+ try {
174
+ const parts = licenseKey.split(".");
175
+ if (parts.length < 3) {
176
+ return {
177
+ isValid: false,
178
+ isTerminalError: false,
179
+ reason: "Incorrect license!"
180
+ };
181
+ }
182
+ const [encodedHeaders, encodedClaims, signature] = parts;
183
+ const plainClaims = new TextDecoder("utf-8").decode(base64url_decode(encodedClaims));
184
+ const claimsJson = JSON.parse(plainClaims);
185
+ const [claimsValidationResult, claimsValidationResultMessage] = validateClaims(publisherId, productId, environmentType, environmentIdentifier, claimsJson);
186
+ if (!claimsValidationResult) {
187
+ return {
188
+ isValid: false,
189
+ isTerminalError: false,
190
+ reason: claimsValidationResultMessage
191
+ };
192
+ }
193
+ const dataToVerify = new TextEncoder().encode(encodedHeaders + "." + encodedClaims);
194
+ const signatureToVerify = base64url_decode(signature);
195
+ for (const publicKey of publicKeys) {
196
+ if (publicKey) {
197
+ let keyImportSuccess = false;
198
+ try {
199
+ const key = yield importRsaKey(publicKey);
200
+ keyImportSuccess = true;
201
+ const isLicenseSignatureValid = yield verifySignature(key, dataToVerify, signatureToVerify);
202
+ if (isLicenseSignatureValid === true) {
203
+ return {
204
+ isValid: true,
205
+ claims: claimsJson
206
+ };
207
+ }
208
+ } catch (e) {
209
+ return {
210
+ isValid: false,
211
+ isTerminalError: false,
212
+ reason: keyImportSuccess ? "An error occured while verifying your license's signature" : "An error occured while importing your public key"
213
+ };
214
+ }
215
+ }
216
+ }
217
+ return {
218
+ isValid: false,
219
+ isTerminalError: false,
220
+ reason: "Invalid license signature: Verification failed!"
221
+ };
222
+ } catch (e) {
223
+ return {
224
+ isValid: false,
225
+ isTerminalError: true,
226
+ reason: "Oops, something went wrong while validating your license"
227
+ };
228
+ }
229
+ });
230
+
231
+ // src/LicenseDialog.tsx
232
+ var React2 = __toESM(require("react"));
233
+ var import_react = require("@fluentui/react");
234
+
235
+ // src/IanusLicenseStateProvider.tsx
236
+ var React = __toESM(require("react"));
237
+ function stateReducer(state, action) {
238
+ switch (action.type) {
239
+ case "setLicense": {
240
+ return __spreadProps(__spreadValues({}, state), { license: action.payload });
241
+ }
242
+ case "setLicenseDialogVisible": {
243
+ return __spreadProps(__spreadValues({}, state), { licenseDialogVisible: action.payload });
244
+ }
245
+ }
246
+ }
247
+ var IanusLicenseState = React.createContext(void 0);
248
+ var IanusLicenseDispatch = React.createContext(void 0);
249
+ var IanusLicenseStateProvider = (props) => {
250
+ const [state, dispatch] = React.useReducer(stateReducer, props != null ? props : {});
251
+ return /* @__PURE__ */ React.createElement(IanusLicenseState.Provider, { value: state }, /* @__PURE__ */ React.createElement(IanusLicenseDispatch.Provider, { value: dispatch }, props.children));
252
+ };
253
+ var useLicenseState = () => {
254
+ const context = React.useContext(IanusLicenseState);
255
+ if (!context) {
256
+ throw new Error("useLicenseState must be used within a state provider!");
257
+ }
258
+ return context;
259
+ };
260
+ var useLicenseDispatch = () => {
261
+ const context = React.useContext(IanusLicenseDispatch);
262
+ if (!context) {
263
+ throw new Error("useLicenseDispatch must be used within a state provider!");
264
+ }
265
+ return context;
266
+ };
267
+ var useLicenseContext = () => {
268
+ return [useLicenseState(), useLicenseDispatch()];
269
+ };
270
+
271
+ // src/LicenseDialog.tsx
272
+ var modalProps = {
273
+ isBlocking: false,
274
+ styles: { main: { maxWidth: 600 } }
275
+ };
276
+ var dialogContentProps = {
277
+ type: import_react.DialogType.largeHeader,
278
+ title: "License"
279
+ };
280
+ var LicenseDialog = ({ publisherId, productId, dataProvider, onCancel, onSubmit }) => {
281
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
282
+ const [licenseState, licenseDispatch] = useLicenseContext();
283
+ const [submitBlocked, setSubmitBlocked] = React2.useState(true);
284
+ const [licenseKeyInput, setLicenseKeyInput] = React2.useState((_a = licenseState.license) == null ? void 0 : _a.licenseKey);
285
+ const [licenseId, setLicenseId] = React2.useState((_b = licenseState == null ? void 0 : licenseState.license) == null ? void 0 : _b.licenseId);
286
+ const [error, setError] = React2.useState("");
287
+ React2.useEffect(() => {
288
+ (() => __async(void 0, null, function* () {
289
+ if (!licenseId) {
290
+ const licenses = yield acquireLicenses(publisherId, productId, dataProvider);
291
+ if (licenses.length > 0) {
292
+ setLicenseId(licenses[0].ian_licenseid);
293
+ }
294
+ }
295
+ }))();
296
+ }, []);
297
+ const tryToExtractDisplayNames = (licenseKey) => {
298
+ var _a2, _b2;
299
+ if (!licenseKey) {
300
+ return null;
301
+ }
302
+ const split = licenseKey.split(".");
303
+ if (split.length != 3) {
304
+ return null;
305
+ }
306
+ const body = split[1];
307
+ try {
308
+ const plainClaims = new TextDecoder("utf-8").decode(base64url_decode(body));
309
+ const claimsJson = JSON.parse(plainClaims);
310
+ if (((_a2 = claimsJson == null ? void 0 : claimsJson.pub_meta) == null ? void 0 : _a2.name) && ((_b2 = claimsJson.prd_meta) == null ? void 0 : _b2.name)) {
311
+ return {
312
+ publisher: claimsJson.pub_meta.name,
313
+ product: claimsJson.prd_meta.name
314
+ };
315
+ } else {
316
+ return null;
317
+ }
318
+ } catch (e) {
319
+ return null;
320
+ }
321
+ };
322
+ const onSubmitClick = () => __async(void 0, null, function* () {
323
+ var _a2, _b2;
324
+ try {
325
+ setSubmitBlocked(true);
326
+ const displayNames = tryToExtractDisplayNames(licenseKeyInput);
327
+ const name = `${(_a2 = displayNames == null ? void 0 : displayNames.publisher) != null ? _a2 : publisherId} - ${(_b2 = displayNames == null ? void 0 : displayNames.product) != null ? _b2 : productId}`;
328
+ const identifier = `${publisherId}_${productId}`;
329
+ if (licenseId) {
330
+ if (isWebApi(dataProvider)) {
331
+ yield dataProvider.updateRecord(
332
+ "ian_license",
333
+ licenseId,
334
+ {
335
+ "ian_name": name,
336
+ "ian_key": licenseKeyInput
337
+ }
338
+ );
339
+ } else if (isDataset(dataProvider)) {
340
+ yield dataProvider.records[licenseId].setValue("ian_name", name);
341
+ yield dataProvider.records[licenseId].setValue("ian_key", licenseKeyInput);
342
+ yield dataProvider.records[licenseId].save();
343
+ }
344
+ } else {
345
+ if (isWebApi(dataProvider)) {
346
+ yield dataProvider.createRecord(
347
+ "ian_license",
348
+ {
349
+ "ian_name": name,
350
+ "ian_identifier": identifier,
351
+ "ian_key": licenseKeyInput
352
+ }
353
+ );
354
+ } else if (isDataset(dataProvider)) {
355
+ const newLicense = yield dataProvider.newRecord();
356
+ yield newLicense.setValue("ian_name", name);
357
+ yield newLicense.setValue("ian_identifier", identifier);
358
+ yield newLicense.setValue("ian_key", licenseKeyInput);
359
+ yield newLicense.save();
360
+ }
361
+ }
362
+ onSubmit();
363
+ } catch (e) {
364
+ setError(e == null ? void 0 : e.message);
365
+ } finally {
366
+ setSubmitBlocked(false);
367
+ }
368
+ });
369
+ return /* @__PURE__ */ React2.createElement(
370
+ import_react.Dialog,
371
+ {
372
+ hidden: !(licenseState == null ? void 0 : licenseState.licenseDialogVisible),
373
+ onDismiss: onCancel,
374
+ dialogContentProps,
375
+ modalProps
376
+ },
377
+ error && /* @__PURE__ */ React2.createElement(
378
+ import_react.MessageBar,
379
+ {
380
+ messageBarType: import_react.MessageBarType.error,
381
+ isMultiline: true,
382
+ styles: { root: { marginBottom: "10px" } },
383
+ onDismiss: () => {
384
+ setError("");
385
+ }
386
+ },
387
+ "An error occured, please try again. Error information: ",
388
+ error
389
+ ),
390
+ /* @__PURE__ */ React2.createElement(import_react.TextField, { label: "License Key", value: licenseKeyInput, onChange: (e, v) => {
391
+ setLicenseKeyInput(v != null ? v : "");
392
+ setSubmitBlocked(false);
393
+ }, required: true }),
394
+ !!((_c = licenseState.license) == null ? void 0 : _c.isValid) && /* @__PURE__ */ React2.createElement(import_react.Text, null, /* @__PURE__ */ React2.createElement("br", null), /* @__PURE__ */ React2.createElement("h3", null, "License Information"), /* @__PURE__ */ React2.createElement("p", null, /* @__PURE__ */ React2.createElement("span", { style: { fontWeight: "bold" } }, "License Publisher: "), " ", /* @__PURE__ */ React2.createElement("span", { title: (_e = (_d = licenseState.license) == null ? void 0 : _d.claims) == null ? void 0 : _e.pub }, (_h = (_g = (_f = licenseState.license) == null ? void 0 : _f.claims) == null ? void 0 : _g.pub_meta) == null ? void 0 : _h.name)), /* @__PURE__ */ React2.createElement("p", null, /* @__PURE__ */ React2.createElement("span", { style: { fontWeight: "bold" } }, "Licensed Product: "), " ", /* @__PURE__ */ React2.createElement("span", { title: (_i = licenseState.license) == null ? void 0 : _i.claims.aud }, (_l = (_k = (_j = licenseState.license) == null ? void 0 : _j.claims) == null ? void 0 : _k.prd_meta) == null ? void 0 : _l.name)), /* @__PURE__ */ React2.createElement("p", null, /* @__PURE__ */ React2.createElement("span", { style: { fontWeight: "bold" } }, "Licensed Customer: "), " ", /* @__PURE__ */ React2.createElement("span", { title: (_m = licenseState.license) == null ? void 0 : _m.claims.sub }, (_p = (_o = (_n = licenseState.license) == null ? void 0 : _n.claims) == null ? void 0 : _o.sub_meta) == null ? void 0 : _p.name)), /* @__PURE__ */ React2.createElement("p", null, /* @__PURE__ */ React2.createElement("span", { style: { fontWeight: "bold" } }, "Licensed Dataverse Environment: "), " ", /* @__PURE__ */ React2.createElement("span", null, (_s = (_r = (_q = licenseState.license) == null ? void 0 : _q.claims) == null ? void 0 : _r.env) == null ? void 0 : _s.map((e) => `${e.identifier} (${e.name})`).join(", "))), /* @__PURE__ */ React2.createElement("p", null, /* @__PURE__ */ React2.createElement("span", { style: { fontWeight: "bold" } }, "License expires after: "), " ", /* @__PURE__ */ React2.createElement("span", null, !((_u = (_t = licenseState.license) == null ? void 0 : _t.claims) == null ? void 0 : _u.exp) ? "Never" : new Date(((_v = licenseState.license) == null ? void 0 : _v.claims.exp) * 1e3).toISOString()))),
395
+ /* @__PURE__ */ React2.createElement(import_react.DialogFooter, null, /* @__PURE__ */ React2.createElement(import_react.PrimaryButton, { onClick: onSubmitClick, text: "Submit", disabled: !licenseKeyInput || submitBlocked }), /* @__PURE__ */ React2.createElement(import_react.DefaultButton, { onClick: onCancel, text: "Cancel" }))
396
+ );
397
+ };
398
+
399
+ // src/IanusGuard.tsx
400
+ var isWebApi = (dataProvider) => {
401
+ return dataProvider.retrieveMultipleRecords !== void 0;
402
+ };
403
+ var isDataset = (dataProvider) => {
404
+ return dataProvider.records !== void 0;
405
+ };
406
+ var acquireLicenses = (publisherId, productId, dataProvider) => __async(void 0, null, function* () {
407
+ const licenseIdentifier = `${publisherId}_${productId}`;
408
+ if (isWebApi(dataProvider)) {
409
+ const response = yield dataProvider.retrieveMultipleRecords("ian_license", `?$filter=ian_identifier eq '${licenseIdentifier}' and statecode eq 0`);
410
+ return response.entities;
411
+ } else if (isDataset(dataProvider)) {
412
+ if (dataProvider.records.length) {
413
+ const record = dataProvider.records[0];
414
+ if (record.getNamedReference().etn !== "ian_license") {
415
+ throw new Error("You need to pass the 'ian_license' entity as data source for LicenseDataset when using a dataset as value");
416
+ }
417
+ }
418
+ return Object.values(dataProvider.records).filter((r) => r.getValue("ian_identifier") === licenseIdentifier).map((r) => dataProvider.columns.reduce((all, cur) => __spreadProps(__spreadValues({}, all), { [cur.name]: r.getValue(cur.name) }), { ian_licenseid: r.getRecordId() }));
419
+ } else {
420
+ throw new Error(`The 'dataProvider' prop must be either of type ComponentFramework.WebApi or ComponentFramework.PropertyTypes.Dataset. You passed '${typeof dataProvider}'.`);
421
+ }
422
+ });
423
+ var updateResultIfDefined = (result, onLicenseValidated) => {
424
+ if (onLicenseValidated) {
425
+ try {
426
+ onLicenseValidated(result);
427
+ } catch (e) {
428
+ if (e && e instanceof Error) {
429
+ console.error(`Error while calling onLicenseValidated: '${e.message}'`);
430
+ }
431
+ }
432
+ }
433
+ };
434
+ var fetchOrganizationIdFromWebApi = (webApi) => __async(void 0, null, function* () {
435
+ const results = yield webApi.retrieveMultipleRecords("organization", "?$top=1&$select=organizationid");
436
+ if (!results.entities.length) {
437
+ return null;
438
+ }
439
+ const organization = results.entities[0];
440
+ return organization.organizationid;
441
+ });
442
+ var IanusGuard = ({ publisherId, productId, publicKeys, environmentType, environmentIdentifier, dataProvider, onLicenseValidated, children }) => {
443
+ var _a, _b, _c, _d;
444
+ const [licenseState, licenseDispatch] = useLicenseContext();
445
+ const onSettingsFinally = () => {
446
+ licenseDispatch({ type: "setLicenseDialogVisible", payload: false });
447
+ initLicenseValidation();
448
+ };
449
+ const runValidation = () => __async(void 0, null, function* () {
450
+ try {
451
+ if (!productId) {
452
+ return {
453
+ isValid: false,
454
+ isTerminalError: true,
455
+ reason: "No productId found, pass a productId as prop!"
456
+ };
457
+ }
458
+ if (!publicKeys || !publicKeys.length) {
459
+ return {
460
+ isValid: false,
461
+ isTerminalError: true,
462
+ reason: "No public key found, pass a valid public key as prop!"
463
+ };
464
+ }
465
+ const licenses = yield acquireLicenses(publisherId, productId, dataProvider);
466
+ if (!licenses.length) {
467
+ return {
468
+ isValid: false,
469
+ isTerminalError: false,
470
+ reason: "No license found!"
471
+ };
472
+ }
473
+ if (licenses.length > 1) {
474
+ return {
475
+ isValid: false,
476
+ isTerminalError: true,
477
+ reason: `Multiple active licenses for '${publisherId}_${productId}' found, please make sure there is only one active license`
478
+ };
479
+ }
480
+ const licenseRecord = licenses[0];
481
+ const resolvedEnvironmentIdentifier = isWebApi(environmentIdentifier) ? yield fetchOrganizationIdFromWebApi(environmentIdentifier) : environmentIdentifier;
482
+ const validationResult = yield validateLicense(publisherId, productId, environmentType, resolvedEnvironmentIdentifier, publicKeys, licenseRecord.ian_key);
483
+ return __spreadProps(__spreadValues({}, validationResult), {
484
+ licenseId: licenseRecord.ian_licenseid,
485
+ licenseKey: licenseRecord.ian_key
486
+ });
487
+ } catch (e) {
488
+ return {
489
+ isValid: false,
490
+ isTerminalError: true,
491
+ reason: e == null ? void 0 : e.message
492
+ };
493
+ }
494
+ });
495
+ const initLicenseValidation = () => __async(void 0, null, function* () {
496
+ const result = yield runValidation();
497
+ licenseDispatch({ type: "setLicense", payload: result });
498
+ updateResultIfDefined(result, onLicenseValidated);
499
+ });
500
+ React3.useEffect(() => {
501
+ if (!isDataset(dataProvider) || !dataProvider.error && !dataProvider.loading && dataProvider.paging.totalResultCount >= 0) {
502
+ initLicenseValidation();
503
+ } else if (dataProvider.error) {
504
+ const result = {
505
+ isValid: false,
506
+ isTerminalError: true,
507
+ reason: `Dataset error: ${dataProvider.errorMessage}`
508
+ };
509
+ updateResultIfDefined(result, onLicenseValidated);
510
+ }
511
+ }, [
512
+ isDataset(dataProvider) ? dataProvider.sortedRecordIds.length : dataProvider
513
+ ]);
514
+ return ((_a = licenseState.license) == null ? void 0 : _a.isValid) ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, licenseState.licenseDialogVisible && /* @__PURE__ */ React3.createElement(LicenseDialog, { publisherId, productId, dataProvider, onSubmit: onSettingsFinally, onCancel: onSettingsFinally }), children) : /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", width: "100%", height: "100%", flex: "1" } }, licenseState.licenseDialogVisible && /* @__PURE__ */ React3.createElement(LicenseDialog, { publisherId, productId, dataProvider, onSubmit: onSettingsFinally, onCancel: onSettingsFinally }), /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1 } }, ((_b = licenseState.license) == null ? void 0 : _b.isValid) === false && /* @__PURE__ */ React3.createElement(
515
+ import_MessageBar.MessageBar,
516
+ {
517
+ messageBarType: import_MessageBar.MessageBarType.error,
518
+ isMultiline: true,
519
+ styles: { root: { marginBottom: "10px" } },
520
+ onDismiss: () => {
521
+ licenseDispatch({ type: "setLicense", payload: void 0 });
522
+ initLicenseValidation();
523
+ },
524
+ actions: /* @__PURE__ */ React3.createElement("div", null, !((_c = licenseState.license) == null ? void 0 : _c.isTerminalError) && /* @__PURE__ */ React3.createElement(import_Button.MessageBarButton, { onClick: () => licenseDispatch({ type: "setLicenseDialogVisible", payload: true }) }, "Set License"))
525
+ },
526
+ "An error occured, please try again. Error information: ",
527
+ (_d = licenseState.license) == null ? void 0 : _d.reason
528
+ ), !licenseState.license && /* @__PURE__ */ React3.createElement(import_Spinner.Spinner, { styles: { root: { width: "auto" } }, label: "Loading..." })));
529
+ };
530
+
531
+ // src/IanusProvider.tsx
532
+ var React4 = __toESM(require("react"));
533
+ var IanusProvider = (props) => {
534
+ return /* @__PURE__ */ React4.createElement(IanusLicenseStateProvider, null, /* @__PURE__ */ React4.createElement(IanusGuard, __spreadValues({}, props)));
535
+ };
536
+
537
+ // ../../../../ianus-core/GuidTools.ts
538
+ var removeCurlyBrackets = (input) => {
539
+ var _a;
540
+ return (_a = input == null ? void 0 : input.replace("{", "")) == null ? void 0 : _a.replace("}", "");
541
+ };
542
+ // Annotate the CommonJS export names for ESM import in node:
543
+ 0 && (module.exports = {
544
+ IanusGuard,
545
+ IanusLicenseStateProvider,
546
+ IanusProvider,
547
+ LicenseDialog,
548
+ acquireLicenses,
549
+ base64url_decode,
550
+ isDataset,
551
+ isWebApi,
552
+ removeCurlyBrackets,
553
+ useLicenseContext,
554
+ useLicenseDispatch,
555
+ useLicenseState,
556
+ validateLicense
557
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@ianua/ianus-dataverse-react-fluentui8",
3
+ "version": "1.0.5",
4
+ "description": "Client-side validation for Ianus Guard licenses in Dataverse using react and FluentUi v8",
5
+ "scripts": {
6
+ "build": "tsup"
7
+ },
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "peerDependencies": {
14
+ "@fluentui/react": "^8",
15
+ "react": "^16.8 || ^17 || ^18",
16
+ "react-dom": "^16.8 || ^17 || ^18"
17
+ },
18
+ "dependencies": {
19
+ "@fluentui/react": "8.29.0",
20
+ "react": "^16.8 || ^17 || ^18",
21
+ "react-dom": "^16.8 || ^17 || ^18"
22
+ },
23
+ "devDependencies": {
24
+ "@microsoft/eslint-plugin-power-apps": "^0.2.33",
25
+ "@types/node": "^18.19.31",
26
+ "@types/powerapps-component-framework": "^1.3.11",
27
+ "@types/react": "^16.14.60",
28
+ "@types/react-dom": "^16.9.24",
29
+ "@typescript-eslint/eslint-plugin": "^7.7.1",
30
+ "@typescript-eslint/parser": "^7.7.1",
31
+ "eslint": "^8.57.0",
32
+ "eslint-plugin-import": "^2.29.1",
33
+ "eslint-plugin-node": "^11.1.0",
34
+ "eslint-plugin-promise": "^6.1.1",
35
+ "eslint-plugin-react": "^7.34.1",
36
+ "pcf-scripts": "^1",
37
+ "pcf-start": "^1",
38
+ "react": "^16.14.0",
39
+ "tsup": "^8.3.5",
40
+ "typescript": "^4.9.5"
41
+ }
42
+ }