@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.
- package/dist/index.d.ts +109 -0
- package/dist/index.js +557 -0
- package/package.json +42 -0
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|