@opentdf/sdk 0.9.0-beta.85 → 0.9.0-beta.87
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/cjs/src/errors.js +14 -2
- package/dist/cjs/src/index.js +8 -2
- package/dist/cjs/src/policy/discovery.js +188 -0
- package/dist/types/src/errors.d.ts +8 -0
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/policy/discovery.d.ts +74 -0
- package/dist/types/src/policy/discovery.d.ts.map +1 -0
- package/dist/web/src/errors.js +12 -1
- package/dist/web/src/index.js +3 -2
- package/dist/web/src/policy/discovery.js +182 -0
- package/package.json +1 -1
- package/src/errors.ts +9 -0
- package/src/index.ts +7 -0
- package/src/policy/discovery.ts +222 -0
package/dist/cjs/src/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.UnsupportedFeatureError = exports.PermissionDeniedError = exports.UnauthenticatedError = exports.ServiceError = exports.NetworkError = exports.UnsafeUrlError = exports.IntegrityError = exports.DecryptError = exports.InvalidFileError = exports.AttributeValidationError = exports.ConfigurationError = exports.TdfError = void 0;
|
|
3
|
+
exports.AttributeNotFoundError = exports.UnsupportedFeatureError = exports.PermissionDeniedError = exports.UnauthenticatedError = exports.ServiceError = exports.NetworkError = exports.UnsafeUrlError = exports.IntegrityError = exports.DecryptError = exports.InvalidFileError = exports.AttributeValidationError = exports.ConfigurationError = exports.TdfError = void 0;
|
|
4
4
|
function scrubCause(error, d) {
|
|
5
5
|
if (!error || (d && d > 4)) {
|
|
6
6
|
return {};
|
|
@@ -138,4 +138,16 @@ class UnsupportedFeatureError extends TdfError {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
exports.UnsupportedFeatureError = UnsupportedFeatureError;
|
|
141
|
-
|
|
141
|
+
/**
|
|
142
|
+
* One or more attribute value FQNs were not found on the platform.
|
|
143
|
+
* Thrown by {@link validateAttributes} and {@link validateAttributeValue} when the platform
|
|
144
|
+
* does not recognize the requested FQNs.
|
|
145
|
+
*/
|
|
146
|
+
class AttributeNotFoundError extends TdfError {
|
|
147
|
+
constructor() {
|
|
148
|
+
super(...arguments);
|
|
149
|
+
this.name = 'AttributeNotFoundError';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.AttributeNotFoundError = AttributeNotFoundError;
|
|
153
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxTQUFTLFVBQVUsQ0FBQyxLQUFhLEVBQUUsQ0FBVTtJQUMzQyxJQUFJLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNCLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUNELElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDaEIsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBQ0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsS0FBSyxDQUFDLEtBQWMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BGLElBQUksS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2xCLEtBQUssQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUNoQyxDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDaEIsS0FBSyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDO0lBQzVCLENBQUM7SUFDRCxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUM7QUFDbkIsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQWEsUUFBUyxTQUFRLEtBQUs7SUFHakMsWUFBWSxPQUFnQixFQUFFLEtBQWE7UUFDekMsS0FBSyxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUgzQixTQUFJLEdBQUcsVUFBVSxDQUFDO1FBSXpCLDRFQUE0RTtRQUM1RSw2SUFBNkk7UUFDN0kseUdBQXlHO1FBQ3pHLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFF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
|
package/dist/cjs/src/index.js
CHANGED
|
@@ -36,13 +36,18 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
36
36
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.ConfigurationError = exports.AttributeValidationError = exports.NetworkError = exports.DecryptError = exports.InvalidFileError = exports.IntegrityError = exports.PermissionDeniedError = exports.TdfError = exports.PlatformClient = exports.tdfSpecVersion = exports.clientType = exports.version = exports.attributeFQNsAsValues = exports.AuthProviders = exports.withHeaders = exports.HttpRequest = void 0;
|
|
39
|
+
exports.ConfigurationError = exports.AttributeNotFoundError = exports.AttributeValidationError = exports.NetworkError = exports.DecryptError = exports.InvalidFileError = exports.IntegrityError = exports.PermissionDeniedError = exports.TdfError = exports.PlatformClient = exports.tdfSpecVersion = exports.clientType = exports.version = exports.attributeValueExists = exports.attributeExists = exports.validateAttributes = exports.listAttributes = exports.attributeFQNsAsValues = exports.AuthProviders = exports.withHeaders = exports.HttpRequest = void 0;
|
|
40
40
|
var auth_js_1 = require("./auth/auth.js");
|
|
41
41
|
Object.defineProperty(exports, "HttpRequest", { enumerable: true, get: function () { return auth_js_1.HttpRequest; } });
|
|
42
42
|
Object.defineProperty(exports, "withHeaders", { enumerable: true, get: function () { return auth_js_1.withHeaders; } });
|
|
43
43
|
exports.AuthProviders = __importStar(require("./auth/providers.js"));
|
|
44
44
|
var api_js_1 = require("./policy/api.js");
|
|
45
45
|
Object.defineProperty(exports, "attributeFQNsAsValues", { enumerable: true, get: function () { return api_js_1.attributeFQNsAsValues; } });
|
|
46
|
+
var discovery_js_1 = require("./policy/discovery.js");
|
|
47
|
+
Object.defineProperty(exports, "listAttributes", { enumerable: true, get: function () { return discovery_js_1.listAttributes; } });
|
|
48
|
+
Object.defineProperty(exports, "validateAttributes", { enumerable: true, get: function () { return discovery_js_1.validateAttributes; } });
|
|
49
|
+
Object.defineProperty(exports, "attributeExists", { enumerable: true, get: function () { return discovery_js_1.attributeExists; } });
|
|
50
|
+
Object.defineProperty(exports, "attributeValueExists", { enumerable: true, get: function () { return discovery_js_1.attributeValueExists; } });
|
|
46
51
|
var version_js_1 = require("./version.js");
|
|
47
52
|
Object.defineProperty(exports, "version", { enumerable: true, get: function () { return version_js_1.version; } });
|
|
48
53
|
Object.defineProperty(exports, "clientType", { enumerable: true, get: function () { return version_js_1.clientType; } });
|
|
@@ -58,7 +63,8 @@ Object.defineProperty(exports, "InvalidFileError", { enumerable: true, get: func
|
|
|
58
63
|
Object.defineProperty(exports, "DecryptError", { enumerable: true, get: function () { return errors_js_1.DecryptError; } });
|
|
59
64
|
Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return errors_js_1.NetworkError; } });
|
|
60
65
|
Object.defineProperty(exports, "AttributeValidationError", { enumerable: true, get: function () { return errors_js_1.AttributeValidationError; } });
|
|
66
|
+
Object.defineProperty(exports, "AttributeNotFoundError", { enumerable: true, get: function () { return errors_js_1.AttributeNotFoundError; } });
|
|
61
67
|
Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_js_1.ConfigurationError; } });
|
|
62
68
|
__exportStar(require("./seekable.js"), exports);
|
|
63
69
|
__exportStar(require("../tdf3/src/models/index.js"), exports);
|
|
64
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsMENBQThGO0FBQWpELHNHQUFBLFdBQVcsT0FBQTtBQUFFLHNHQUFBLFdBQVcsT0FBQTtBQUNyRSxxRUFBcUQ7QUFDckQsMENBQXdEO0FBQS9DLCtHQUFBLHFCQUFxQixPQUFBO0FBQzlCLHNEQUsrQjtBQUo3Qiw4R0FBQSxjQUFjLE9BQUE7QUFDZCxrSEFBQSxrQkFBa0IsT0FBQTtBQUNsQiwrR0FBQSxlQUFlLE9BQUE7QUFDZixvSEFBQSxvQkFBb0IsT0FBQTtBQUV0QiwyQ0FBbUU7QUFBMUQscUdBQUEsT0FBTyxPQUFBO0FBQUUsd0dBQUEsVUFBVSxPQUFBO0FBQUUsNEdBQUEsY0FBYyxPQUFBO0FBQzVDLDZDQUFrRztBQUF6Riw2R0FBQSxjQUFjLE9BQUE7QUFDdkIsK0NBQTZCO0FBQzdCLHlDQVVxQjtBQVRuQixxR0FBQSxRQUFRLE9BQUE7QUFDUixrSEFBQSxxQkFBcUIsT0FBQTtBQUNyQiwyR0FBQSxjQUFjLE9BQUE7QUFDZCw2R0FBQSxnQkFBZ0IsT0FBQTtBQUNoQix5R0FBQSxZQUFZLE9BQUE7QUFDWix5R0FBQSxZQUFZLE9BQUE7QUFDWixxSEFBQSx3QkFBd0IsT0FBQTtBQUN4QixtSEFBQSxzQkFBc0IsT0FBQTtBQUN0QiwrR0FBQSxrQkFBa0IsT0FBQTtBQUVwQixnREFBOEI7QUFDOUIsOERBQTRDIn0=
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listAttributes = listAttributes;
|
|
4
|
+
exports.validateAttributes = validateAttributes;
|
|
5
|
+
exports.attributeExists = attributeExists;
|
|
6
|
+
exports.attributeValueExists = attributeValueExists;
|
|
7
|
+
const connect_1 = require("@connectrpc/connect");
|
|
8
|
+
const errors_js_1 = require("../errors.js");
|
|
9
|
+
const utils_js_1 = require("../utils.js");
|
|
10
|
+
const platform_js_1 = require("../platform.js");
|
|
11
|
+
// Caps the pagination loop in listAttributes. 10 pages × 1000 records = 10,000
|
|
12
|
+
// attributes maximum, which is generous for browser use while preventing runaway
|
|
13
|
+
// memory growth if a server repeatedly returns a non-zero next_offset.
|
|
14
|
+
const MAX_LIST_ATTRIBUTES_PAGES = 10;
|
|
15
|
+
// Number of attributes to request per page. Matches the platform's default
|
|
16
|
+
// (ListRequestLimitDefault = 1000) so behavior is stable regardless of server config.
|
|
17
|
+
const LIST_ATTRIBUTES_PAGE_SIZE = 1000;
|
|
18
|
+
// Matches the server-side proto constraint: GetAttributeValuesByFqnsRequest has
|
|
19
|
+
// max_items: 250 on the fqns field, so the client rejects oversized requests
|
|
20
|
+
// locally instead of receiving a cryptic server validation error.
|
|
21
|
+
const MAX_VALIDATE_FQNS = 250;
|
|
22
|
+
// Attribute value FQN format: https://<namespace>/attr/<name>/value/<value>
|
|
23
|
+
// Restricts to safe URL characters to prevent XSS via FQNs in error messages
|
|
24
|
+
const ATTRIBUTE_VALUE_FQN_RE = /^https?:\/\/[a-zA-Z0-9._~%-]+\/attr\/[a-zA-Z0-9._~%-]+\/value\/[a-zA-Z0-9._~%-]+$/i;
|
|
25
|
+
// Attribute-level FQN format: https://<namespace>/attr/<name> (no /value/ segment)
|
|
26
|
+
// Restricts to safe URL characters to prevent XSS via FQNs in error messages
|
|
27
|
+
const ATTRIBUTE_FQN_RE = /^https?:\/\/[a-zA-Z0-9._~%-]+\/attr\/[a-zA-Z0-9._~%-]+$/i;
|
|
28
|
+
/**
|
|
29
|
+
* Returns all active attributes available on the platform, auto-paginating through all results.
|
|
30
|
+
* An optional namespace name or ID may be provided to filter results.
|
|
31
|
+
*
|
|
32
|
+
* Use this before calling `createZTDF()` to see what attributes are available for data tagging.
|
|
33
|
+
*
|
|
34
|
+
* @param platformUrl The platform base URL.
|
|
35
|
+
* @param authProvider An auth provider for the request.
|
|
36
|
+
* @param namespace Optional namespace name or ID to filter results.
|
|
37
|
+
* @returns All active {@link Attribute} objects on the platform.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const attrs = await listAttributes(platformUrl, authProvider);
|
|
42
|
+
* for (const a of attrs) {
|
|
43
|
+
* console.log(a.fqn);
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
async function listAttributes(platformUrl, authProvider, namespace) {
|
|
48
|
+
if (!(0, utils_js_1.validateSecureUrl)(platformUrl)) {
|
|
49
|
+
throw new errors_js_1.ConfigurationError('platformUrl must use HTTPS protocol');
|
|
50
|
+
}
|
|
51
|
+
const platform = new platform_js_1.PlatformClient({ authProvider, platformUrl });
|
|
52
|
+
const result = [];
|
|
53
|
+
let nextOffset = 0;
|
|
54
|
+
for (let pages = 0; pages < MAX_LIST_ATTRIBUTES_PAGES; pages++) {
|
|
55
|
+
let resp;
|
|
56
|
+
try {
|
|
57
|
+
resp = await platform.v1.attributes.listAttributes({
|
|
58
|
+
namespace: namespace ?? '',
|
|
59
|
+
pagination: { offset: nextOffset, limit: LIST_ATTRIBUTES_PAGE_SIZE },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
throw new errors_js_1.NetworkError(`[ListAttributes] ${(0, utils_js_1.extractRpcErrorMessage)(e)}`);
|
|
64
|
+
}
|
|
65
|
+
result.push(...resp.attributes);
|
|
66
|
+
nextOffset = resp.pagination?.nextOffset ?? 0;
|
|
67
|
+
if (nextOffset === 0) {
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
throw new errors_js_1.ConfigurationError(`listAttributes returned more than ${MAX_LIST_ATTRIBUTES_PAGES * LIST_ATTRIBUTES_PAGE_SIZE} attributes. Use the namespace parameter to narrow results.`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Checks that all provided attribute value FQNs exist on the platform.
|
|
75
|
+
* Validates FQN format first, then verifies existence via the platform API.
|
|
76
|
+
*
|
|
77
|
+
* Use this before `createZTDF()` to catch missing or misspelled attributes early
|
|
78
|
+
* instead of discovering the problem at decryption time.
|
|
79
|
+
*
|
|
80
|
+
* @param platformUrl The platform base URL.
|
|
81
|
+
* @param authProvider An auth provider for the request.
|
|
82
|
+
* @param fqns Attribute value FQNs to validate, in the form
|
|
83
|
+
* `https://<namespace>/attr/<name>/value/<value>`.
|
|
84
|
+
* @throws {@link AttributeNotFoundError} if any FQNs are not found on the platform.
|
|
85
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or there are too many FQNs.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* await validateAttributes(platformUrl, authProvider, [
|
|
90
|
+
* 'https://opentdf.io/attr/department/value/marketing',
|
|
91
|
+
* ]);
|
|
92
|
+
* // Safe to encrypt — all attributes confirmed present
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
async function validateAttributes(platformUrl, authProvider, fqns) {
|
|
96
|
+
if (!fqns || fqns.length === 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!(0, utils_js_1.validateSecureUrl)(platformUrl)) {
|
|
100
|
+
throw new errors_js_1.ConfigurationError('platformUrl must use HTTPS protocol');
|
|
101
|
+
}
|
|
102
|
+
if (fqns.length > MAX_VALIDATE_FQNS) {
|
|
103
|
+
throw new errors_js_1.ConfigurationError(`too many attribute FQNs: ${fqns.length} exceeds maximum of ${MAX_VALIDATE_FQNS}`);
|
|
104
|
+
}
|
|
105
|
+
for (const fqn of fqns) {
|
|
106
|
+
if (!ATTRIBUTE_VALUE_FQN_RE.test(fqn)) {
|
|
107
|
+
throw new errors_js_1.ConfigurationError('invalid attribute value FQN format');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const platform = new platform_js_1.PlatformClient({ authProvider, platformUrl });
|
|
111
|
+
let resp;
|
|
112
|
+
try {
|
|
113
|
+
resp = await platform.v1.attributes.getAttributeValuesByFqns({ fqns });
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
throw new errors_js_1.NetworkError(`[GetAttributeValuesByFqns] ${(0, utils_js_1.extractRpcErrorMessage)(e)}`);
|
|
117
|
+
}
|
|
118
|
+
const found = resp.fqnAttributeValues;
|
|
119
|
+
const missing = fqns.filter((fqn) => !(fqn in found));
|
|
120
|
+
if (missing.length > 0) {
|
|
121
|
+
throw new errors_js_1.AttributeNotFoundError(`attribute not found: ${missing.length} FQN(s) missing`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Reports whether the attribute definition identified by `attributeFqn` exists on the platform.
|
|
126
|
+
*
|
|
127
|
+
* `attributeFqn` should be an attribute-level FQN (no `/value/` segment):
|
|
128
|
+
* `https://<namespace>/attr/<attribute_name>`
|
|
129
|
+
*
|
|
130
|
+
* @param platformUrl The platform base URL.
|
|
131
|
+
* @param authProvider An auth provider for the request.
|
|
132
|
+
* @param attributeFqn The attribute-level FQN to check.
|
|
133
|
+
* @returns `true` if the attribute exists, `false` if it does not.
|
|
134
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
135
|
+
* @throws {@link NetworkError} if a non-not-found service error occurs.
|
|
136
|
+
*/
|
|
137
|
+
async function attributeExists(platformUrl, authProvider, attributeFqn) {
|
|
138
|
+
if (!(0, utils_js_1.validateSecureUrl)(platformUrl)) {
|
|
139
|
+
throw new errors_js_1.ConfigurationError('platformUrl must use HTTPS protocol');
|
|
140
|
+
}
|
|
141
|
+
if (!ATTRIBUTE_FQN_RE.test(attributeFqn)) {
|
|
142
|
+
throw new errors_js_1.ConfigurationError('invalid attribute FQN format');
|
|
143
|
+
}
|
|
144
|
+
const platform = new platform_js_1.PlatformClient({ authProvider, platformUrl });
|
|
145
|
+
try {
|
|
146
|
+
await platform.v1.attributes.getAttribute({
|
|
147
|
+
identifier: { case: 'fqn', value: attributeFqn },
|
|
148
|
+
});
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
if (e instanceof connect_1.ConnectError && e.code === connect_1.Code.NotFound) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
throw new errors_js_1.NetworkError(`[GetAttribute] ${(0, utils_js_1.extractRpcErrorMessage)(e)}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Reports whether the attribute value FQN exists on the platform.
|
|
160
|
+
*
|
|
161
|
+
* `valueFqn` should be a full attribute value FQN (with `/value/` segment):
|
|
162
|
+
* `https://<namespace>/attr/<attribute_name>/value/<value>`
|
|
163
|
+
*
|
|
164
|
+
* @param platformUrl The platform base URL.
|
|
165
|
+
* @param authProvider An auth provider for the request.
|
|
166
|
+
* @param valueFqn The attribute value FQN to check.
|
|
167
|
+
* @returns `true` if the value exists, `false` if it does not.
|
|
168
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
169
|
+
* @throws {@link NetworkError} if a service error occurs.
|
|
170
|
+
*/
|
|
171
|
+
async function attributeValueExists(platformUrl, authProvider, valueFqn) {
|
|
172
|
+
if (!(0, utils_js_1.validateSecureUrl)(platformUrl)) {
|
|
173
|
+
throw new errors_js_1.ConfigurationError('platformUrl must use HTTPS protocol');
|
|
174
|
+
}
|
|
175
|
+
if (!ATTRIBUTE_VALUE_FQN_RE.test(valueFqn)) {
|
|
176
|
+
throw new errors_js_1.ConfigurationError('invalid attribute value FQN format');
|
|
177
|
+
}
|
|
178
|
+
const platform = new platform_js_1.PlatformClient({ authProvider, platformUrl });
|
|
179
|
+
let resp;
|
|
180
|
+
try {
|
|
181
|
+
resp = await platform.v1.attributes.getAttributeValuesByFqns({ fqns: [valueFqn] });
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
throw new errors_js_1.NetworkError(`[GetAttributeValuesByFqns] ${(0, utils_js_1.extractRpcErrorMessage)(e)}`);
|
|
185
|
+
}
|
|
186
|
+
return valueFqn in resp.fqnAttributeValues;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY292ZXJ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3BvbGljeS9kaXNjb3ZlcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFpREEsd0NBaUNDO0FBd0JELGdEQXNDQztBQWVELDBDQXlCQztBQWVELG9EQXNCQztBQTdORCxpREFBeUQ7QUFDekQsNENBQXdGO0FBRXhGLDBDQUF3RTtBQUN4RSxnREFBZ0Q7QUFHaEQsK0VBQStFO0FBQy9FLGlGQUFpRjtBQUNqRix1RUFBdUU7QUFDdkUsTUFBTSx5QkFBeUIsR0FBRyxFQUFFLENBQUM7QUFFckMsMkVBQTJFO0FBQzNFLHNGQUFzRjtBQUN0RixNQUFNLHlCQUF5QixHQUFHLElBQUksQ0FBQztBQUV2QyxnRkFBZ0Y7QUFDaEYsNkVBQTZFO0FBQzdFLGtFQUFrRTtBQUNsRSxNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQUU5Qiw0RUFBNEU7QUFDNUUsNkVBQTZFO0FBQzdFLE1BQU0sc0JBQXNCLEdBQzFCLG9GQUFvRixDQUFDO0FBRXZGLG9GQUFvRjtBQUNwRiw2RUFBNkU7QUFDN0UsTUFBTSxnQkFBZ0IsR0FBRywwREFBMEQsQ0FBQztBQUVwRjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0ksS0FBSyxVQUFVLGNBQWMsQ0FDbEMsV0FBbUIsRUFDbkIsWUFBMEIsRUFDMUIsU0FBa0I7SUFFbEIsSUFBSSxDQUFDLElBQUEsNEJBQWlCLEVBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztRQUNwQyxNQUFNLElBQUksOEJBQWtCLENBQUMscUNBQXFDLENBQUMsQ0FBQztJQUN0RSxDQUFDO0lBQ0QsTUFBTSxRQUFRLEdBQUcsSUFBSSw0QkFBYyxDQUFDLEVBQUUsWUFBWSxFQUFFLFdBQVcsRUFBRSxDQUFDLENBQUM7SUFDbkUsTUFBTSxNQUFNLEdBQWdCLEVBQUUsQ0FBQztJQUMvQixJQUFJLFVBQVUsR0FBRyxDQUFDLENBQUM7SUFFbkIsS0FBSyxJQUFJLEtB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
|
|
@@ -71,4 +71,12 @@ export declare class PermissionDeniedError extends TdfError {
|
|
|
71
71
|
export declare class UnsupportedFeatureError extends TdfError {
|
|
72
72
|
name: string;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* One or more attribute value FQNs were not found on the platform.
|
|
76
|
+
* Thrown by {@link validateAttributes} and {@link validateAttributeValue} when the platform
|
|
77
|
+
* does not recognize the requested FQNs.
|
|
78
|
+
*/
|
|
79
|
+
export declare class AttributeNotFoundError extends TdfError {
|
|
80
|
+
name: string;
|
|
81
|
+
}
|
|
74
82
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/errors.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACxB,IAAI,SAAc;gBAEf,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;CAO5C;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;IACrC,IAAI,SAAwB;CACtC;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,kBAAkB;IACrD,IAAI,SAA8B;IAC3C,SAAS,EAAE,OAAO,CAAC;gBACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK;CAI/D;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;CAAG;AAEjD;;GAEG;AACH,qBAAa,YAAa,SAAQ,gBAAgB;IACvC,IAAI,SAAkB;CAChC;AAED,qBAAa,cAAe,SAAQ,gBAAgB;IACzC,IAAI,SAAoB;CAClC;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,gBAAgB;IACzC,IAAI,SAAoB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE;CAK9C;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IAC/B,IAAI,SAAkB;CAChC;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IAC/B,IAAI,SAAkB;CAChC;AAED,mCAAmC;AACnC,qBAAa,oBAAqB,SAAQ,QAAQ;IACvC,IAAI,SAA0B;CACxC;AAED,kCAAkC;AAClC,qBAAa,qBAAsB,SAAQ,QAAQ;IACxC,IAAI,SAA2B;IACxC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;gBAE5B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,KAAK;CAMnE;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,QAAQ;IAC1C,IAAI,SAA6B;CAC3C"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/errors.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACxB,IAAI,SAAc;gBAEf,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;CAO5C;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;IACrC,IAAI,SAAwB;CACtC;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,kBAAkB;IACrD,IAAI,SAA8B;IAC3C,SAAS,EAAE,OAAO,CAAC;gBACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK;CAI/D;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;CAAG;AAEjD;;GAEG;AACH,qBAAa,YAAa,SAAQ,gBAAgB;IACvC,IAAI,SAAkB;CAChC;AAED,qBAAa,cAAe,SAAQ,gBAAgB;IACzC,IAAI,SAAoB;CAClC;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,gBAAgB;IACzC,IAAI,SAAoB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE;CAK9C;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IAC/B,IAAI,SAAkB;CAChC;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IAC/B,IAAI,SAAkB;CAChC;AAED,mCAAmC;AACnC,qBAAa,oBAAqB,SAAQ,QAAQ;IACvC,IAAI,SAA0B;CACxC;AAED,kCAAkC;AAClC,qBAAa,qBAAsB,SAAQ,QAAQ;IACxC,IAAI,SAA2B;IACxC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;gBAE5B,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,KAAK;CAMnE;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,QAAQ;IAC1C,IAAI,SAA6B;CAC3C;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,QAAQ;IACzC,IAAI,SAA4B;CAC1C"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { type AuthProvider, type HttpMethod, HttpRequest, withHeaders } from './auth/auth.js';
|
|
2
2
|
export * as AuthProviders from './auth/providers.js';
|
|
3
3
|
export { attributeFQNsAsValues } from './policy/api.js';
|
|
4
|
+
export { listAttributes, validateAttributes, attributeExists, attributeValueExists, } from './policy/discovery.js';
|
|
4
5
|
export { version, clientType, tdfSpecVersion } from './version.js';
|
|
5
6
|
export { PlatformClient, type PlatformClientOptions, type PlatformServices } from './platform.js';
|
|
6
7
|
export * from './opentdf.js';
|
|
7
|
-
export { TdfError, PermissionDeniedError, IntegrityError, InvalidFileError, DecryptError, NetworkError, AttributeValidationError, ConfigurationError, } from './errors.js';
|
|
8
|
+
export { TdfError, PermissionDeniedError, IntegrityError, InvalidFileError, DecryptError, NetworkError, AttributeValidationError, AttributeNotFoundError, ConfigurationError, } from './errors.js';
|
|
8
9
|
export * from './seekable.js';
|
|
9
10
|
export * from '../tdf3/src/models/index.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC9F,OAAO,KAAK,aAAa,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAClG,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,cAAc,eAAe,CAAC;AAC9B,cAAc,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC9F,OAAO,KAAK,aAAa,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAClG,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,cAAc,eAAe,CAAC;AAC9B,cAAc,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type AuthProvider } from '../auth/auth.js';
|
|
2
|
+
import type { Attribute } from '../platform/policy/objects_pb.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns all active attributes available on the platform, auto-paginating through all results.
|
|
5
|
+
* An optional namespace name or ID may be provided to filter results.
|
|
6
|
+
*
|
|
7
|
+
* Use this before calling `createZTDF()` to see what attributes are available for data tagging.
|
|
8
|
+
*
|
|
9
|
+
* @param platformUrl The platform base URL.
|
|
10
|
+
* @param authProvider An auth provider for the request.
|
|
11
|
+
* @param namespace Optional namespace name or ID to filter results.
|
|
12
|
+
* @returns All active {@link Attribute} objects on the platform.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const attrs = await listAttributes(platformUrl, authProvider);
|
|
17
|
+
* for (const a of attrs) {
|
|
18
|
+
* console.log(a.fqn);
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function listAttributes(platformUrl: string, authProvider: AuthProvider, namespace?: string): Promise<Attribute[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Checks that all provided attribute value FQNs exist on the platform.
|
|
25
|
+
* Validates FQN format first, then verifies existence via the platform API.
|
|
26
|
+
*
|
|
27
|
+
* Use this before `createZTDF()` to catch missing or misspelled attributes early
|
|
28
|
+
* instead of discovering the problem at decryption time.
|
|
29
|
+
*
|
|
30
|
+
* @param platformUrl The platform base URL.
|
|
31
|
+
* @param authProvider An auth provider for the request.
|
|
32
|
+
* @param fqns Attribute value FQNs to validate, in the form
|
|
33
|
+
* `https://<namespace>/attr/<name>/value/<value>`.
|
|
34
|
+
* @throws {@link AttributeNotFoundError} if any FQNs are not found on the platform.
|
|
35
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or there are too many FQNs.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* await validateAttributes(platformUrl, authProvider, [
|
|
40
|
+
* 'https://opentdf.io/attr/department/value/marketing',
|
|
41
|
+
* ]);
|
|
42
|
+
* // Safe to encrypt — all attributes confirmed present
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateAttributes(platformUrl: string, authProvider: AuthProvider, fqns: string[]): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Reports whether the attribute definition identified by `attributeFqn` exists on the platform.
|
|
48
|
+
*
|
|
49
|
+
* `attributeFqn` should be an attribute-level FQN (no `/value/` segment):
|
|
50
|
+
* `https://<namespace>/attr/<attribute_name>`
|
|
51
|
+
*
|
|
52
|
+
* @param platformUrl The platform base URL.
|
|
53
|
+
* @param authProvider An auth provider for the request.
|
|
54
|
+
* @param attributeFqn The attribute-level FQN to check.
|
|
55
|
+
* @returns `true` if the attribute exists, `false` if it does not.
|
|
56
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
57
|
+
* @throws {@link NetworkError} if a non-not-found service error occurs.
|
|
58
|
+
*/
|
|
59
|
+
export declare function attributeExists(platformUrl: string, authProvider: AuthProvider, attributeFqn: string): Promise<boolean>;
|
|
60
|
+
/**
|
|
61
|
+
* Reports whether the attribute value FQN exists on the platform.
|
|
62
|
+
*
|
|
63
|
+
* `valueFqn` should be a full attribute value FQN (with `/value/` segment):
|
|
64
|
+
* `https://<namespace>/attr/<attribute_name>/value/<value>`
|
|
65
|
+
*
|
|
66
|
+
* @param platformUrl The platform base URL.
|
|
67
|
+
* @param authProvider An auth provider for the request.
|
|
68
|
+
* @param valueFqn The attribute value FQN to check.
|
|
69
|
+
* @returns `true` if the value exists, `false` if it does not.
|
|
70
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
71
|
+
* @throws {@link NetworkError} if a service error occurs.
|
|
72
|
+
*/
|
|
73
|
+
export declare function attributeValueExists(platformUrl: string, authProvider: AuthProvider, valueFqn: string): Promise<boolean>;
|
|
74
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../../../src/policy/discovery.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAyBlE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,YAAY,EAC1B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,EAAE,CAAC,CA6BtB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,IAAI,CAAC,CAkCf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CAqBlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAkBlB"}
|
package/dist/web/src/errors.js
CHANGED
|
@@ -123,4 +123,15 @@ export class UnsupportedFeatureError extends TdfError {
|
|
|
123
123
|
this.name = 'UnsupportedFeatureError';
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
|
|
126
|
+
/**
|
|
127
|
+
* One or more attribute value FQNs were not found on the platform.
|
|
128
|
+
* Thrown by {@link validateAttributes} and {@link validateAttributeValue} when the platform
|
|
129
|
+
* does not recognize the requested FQNs.
|
|
130
|
+
*/
|
|
131
|
+
export class AttributeNotFoundError extends TdfError {
|
|
132
|
+
constructor() {
|
|
133
|
+
super(...arguments);
|
|
134
|
+
this.name = 'AttributeNotFoundError';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxTQUFTLFVBQVUsQ0FBQyxLQUFhLEVBQUUsQ0FBVTtJQUMzQyxJQUFJLENBQUMsS0FBSyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNCLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUNELElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDaEIsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBQ0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsS0FBSyxDQUFDLEtBQWMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BGLElBQUksS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2xCLEtBQUssQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUNoQyxDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDaEIsS0FBSyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDO0lBQzVCLENBQUM7SUFDRCxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUM7QUFDbkIsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxRQUFTLFNBQVEsS0FBSztJQUdqQyxZQUFZLE9BQWdCLEVBQUUsS0FBYTtRQUN6QyxLQUFLLENBQUMsT0FBTyxFQUFFLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBSDNCLFNBQUksR0FBRyxVQUFVLENBQUM7UUFJekIsNEVBQTRFO1FBQzVFLDZJQUE2STtRQUM3SSx5R0FBeUc7UUFDekcsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVB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
|
package/dist/web/src/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { HttpRequest, withHeaders } from './auth/auth.js';
|
|
2
2
|
export * as AuthProviders from './auth/providers.js';
|
|
3
3
|
export { attributeFQNsAsValues } from './policy/api.js';
|
|
4
|
+
export { listAttributes, validateAttributes, attributeExists, attributeValueExists, } from './policy/discovery.js';
|
|
4
5
|
export { version, clientType, tdfSpecVersion } from './version.js';
|
|
5
6
|
export { PlatformClient } from './platform.js';
|
|
6
7
|
export * from './opentdf.js';
|
|
7
|
-
export { TdfError, PermissionDeniedError, IntegrityError, InvalidFileError, DecryptError, NetworkError, AttributeValidationError, ConfigurationError, } from './errors.js';
|
|
8
|
+
export { TdfError, PermissionDeniedError, IntegrityError, InvalidFileError, DecryptError, NetworkError, AttributeValidationError, AttributeNotFoundError, ConfigurationError, } from './errors.js';
|
|
8
9
|
export * from './seekable.js';
|
|
9
10
|
export * from '../tdf3/src/models/index.js';
|
|
10
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFzQyxXQUFXLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUYsT0FBTyxLQUFLLGFBQWEsTUFBTSxxQkFBcUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN4RCxPQUFPLEVBQ0wsY0FBYyxFQUNkLGtCQUFrQixFQUNsQixlQUFlLEVBQ2Ysb0JBQW9CLEdBQ3JCLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ25FLE9BQU8sRUFBRSxjQUFjLEVBQXFELE1BQU0sZUFBZSxDQUFDO0FBQ2xHLGNBQWMsY0FBYyxDQUFDO0FBQzdCLE9BQU8sRUFDTCxRQUFRLEVBQ1IscUJBQXFCLEVBQ3JCLGNBQWMsRUFDZCxnQkFBZ0IsRUFDaEIsWUFBWSxFQUNaLFlBQVksRUFDWix3QkFBd0IsRUFDeEIsc0JBQXNCLEVBQ3RCLGtCQUFrQixHQUNuQixNQUFNLGFBQWEsQ0FBQztBQUNyQixjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLDZCQUE2QixDQUFDIn0=
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { ConnectError, Code } from '@connectrpc/connect';
|
|
2
|
+
import { AttributeNotFoundError, ConfigurationError, NetworkError } from '../errors.js';
|
|
3
|
+
import { extractRpcErrorMessage, validateSecureUrl } from '../utils.js';
|
|
4
|
+
import { PlatformClient } from '../platform.js';
|
|
5
|
+
// Caps the pagination loop in listAttributes. 10 pages × 1000 records = 10,000
|
|
6
|
+
// attributes maximum, which is generous for browser use while preventing runaway
|
|
7
|
+
// memory growth if a server repeatedly returns a non-zero next_offset.
|
|
8
|
+
const MAX_LIST_ATTRIBUTES_PAGES = 10;
|
|
9
|
+
// Number of attributes to request per page. Matches the platform's default
|
|
10
|
+
// (ListRequestLimitDefault = 1000) so behavior is stable regardless of server config.
|
|
11
|
+
const LIST_ATTRIBUTES_PAGE_SIZE = 1000;
|
|
12
|
+
// Matches the server-side proto constraint: GetAttributeValuesByFqnsRequest has
|
|
13
|
+
// max_items: 250 on the fqns field, so the client rejects oversized requests
|
|
14
|
+
// locally instead of receiving a cryptic server validation error.
|
|
15
|
+
const MAX_VALIDATE_FQNS = 250;
|
|
16
|
+
// Attribute value FQN format: https://<namespace>/attr/<name>/value/<value>
|
|
17
|
+
// Restricts to safe URL characters to prevent XSS via FQNs in error messages
|
|
18
|
+
const ATTRIBUTE_VALUE_FQN_RE = /^https?:\/\/[a-zA-Z0-9._~%-]+\/attr\/[a-zA-Z0-9._~%-]+\/value\/[a-zA-Z0-9._~%-]+$/i;
|
|
19
|
+
// Attribute-level FQN format: https://<namespace>/attr/<name> (no /value/ segment)
|
|
20
|
+
// Restricts to safe URL characters to prevent XSS via FQNs in error messages
|
|
21
|
+
const ATTRIBUTE_FQN_RE = /^https?:\/\/[a-zA-Z0-9._~%-]+\/attr\/[a-zA-Z0-9._~%-]+$/i;
|
|
22
|
+
/**
|
|
23
|
+
* Returns all active attributes available on the platform, auto-paginating through all results.
|
|
24
|
+
* An optional namespace name or ID may be provided to filter results.
|
|
25
|
+
*
|
|
26
|
+
* Use this before calling `createZTDF()` to see what attributes are available for data tagging.
|
|
27
|
+
*
|
|
28
|
+
* @param platformUrl The platform base URL.
|
|
29
|
+
* @param authProvider An auth provider for the request.
|
|
30
|
+
* @param namespace Optional namespace name or ID to filter results.
|
|
31
|
+
* @returns All active {@link Attribute} objects on the platform.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const attrs = await listAttributes(platformUrl, authProvider);
|
|
36
|
+
* for (const a of attrs) {
|
|
37
|
+
* console.log(a.fqn);
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export async function listAttributes(platformUrl, authProvider, namespace) {
|
|
42
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
43
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
44
|
+
}
|
|
45
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
46
|
+
const result = [];
|
|
47
|
+
let nextOffset = 0;
|
|
48
|
+
for (let pages = 0; pages < MAX_LIST_ATTRIBUTES_PAGES; pages++) {
|
|
49
|
+
let resp;
|
|
50
|
+
try {
|
|
51
|
+
resp = await platform.v1.attributes.listAttributes({
|
|
52
|
+
namespace: namespace ?? '',
|
|
53
|
+
pagination: { offset: nextOffset, limit: LIST_ATTRIBUTES_PAGE_SIZE },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
throw new NetworkError(`[ListAttributes] ${extractRpcErrorMessage(e)}`);
|
|
58
|
+
}
|
|
59
|
+
result.push(...resp.attributes);
|
|
60
|
+
nextOffset = resp.pagination?.nextOffset ?? 0;
|
|
61
|
+
if (nextOffset === 0) {
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw new ConfigurationError(`listAttributes returned more than ${MAX_LIST_ATTRIBUTES_PAGES * LIST_ATTRIBUTES_PAGE_SIZE} attributes. Use the namespace parameter to narrow results.`);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Checks that all provided attribute value FQNs exist on the platform.
|
|
69
|
+
* Validates FQN format first, then verifies existence via the platform API.
|
|
70
|
+
*
|
|
71
|
+
* Use this before `createZTDF()` to catch missing or misspelled attributes early
|
|
72
|
+
* instead of discovering the problem at decryption time.
|
|
73
|
+
*
|
|
74
|
+
* @param platformUrl The platform base URL.
|
|
75
|
+
* @param authProvider An auth provider for the request.
|
|
76
|
+
* @param fqns Attribute value FQNs to validate, in the form
|
|
77
|
+
* `https://<namespace>/attr/<name>/value/<value>`.
|
|
78
|
+
* @throws {@link AttributeNotFoundError} if any FQNs are not found on the platform.
|
|
79
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or there are too many FQNs.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* await validateAttributes(platformUrl, authProvider, [
|
|
84
|
+
* 'https://opentdf.io/attr/department/value/marketing',
|
|
85
|
+
* ]);
|
|
86
|
+
* // Safe to encrypt — all attributes confirmed present
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export async function validateAttributes(platformUrl, authProvider, fqns) {
|
|
90
|
+
if (!fqns || fqns.length === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
94
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
95
|
+
}
|
|
96
|
+
if (fqns.length > MAX_VALIDATE_FQNS) {
|
|
97
|
+
throw new ConfigurationError(`too many attribute FQNs: ${fqns.length} exceeds maximum of ${MAX_VALIDATE_FQNS}`);
|
|
98
|
+
}
|
|
99
|
+
for (const fqn of fqns) {
|
|
100
|
+
if (!ATTRIBUTE_VALUE_FQN_RE.test(fqn)) {
|
|
101
|
+
throw new ConfigurationError('invalid attribute value FQN format');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
105
|
+
let resp;
|
|
106
|
+
try {
|
|
107
|
+
resp = await platform.v1.attributes.getAttributeValuesByFqns({ fqns });
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
throw new NetworkError(`[GetAttributeValuesByFqns] ${extractRpcErrorMessage(e)}`);
|
|
111
|
+
}
|
|
112
|
+
const found = resp.fqnAttributeValues;
|
|
113
|
+
const missing = fqns.filter((fqn) => !(fqn in found));
|
|
114
|
+
if (missing.length > 0) {
|
|
115
|
+
throw new AttributeNotFoundError(`attribute not found: ${missing.length} FQN(s) missing`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Reports whether the attribute definition identified by `attributeFqn` exists on the platform.
|
|
120
|
+
*
|
|
121
|
+
* `attributeFqn` should be an attribute-level FQN (no `/value/` segment):
|
|
122
|
+
* `https://<namespace>/attr/<attribute_name>`
|
|
123
|
+
*
|
|
124
|
+
* @param platformUrl The platform base URL.
|
|
125
|
+
* @param authProvider An auth provider for the request.
|
|
126
|
+
* @param attributeFqn The attribute-level FQN to check.
|
|
127
|
+
* @returns `true` if the attribute exists, `false` if it does not.
|
|
128
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
129
|
+
* @throws {@link NetworkError} if a non-not-found service error occurs.
|
|
130
|
+
*/
|
|
131
|
+
export async function attributeExists(platformUrl, authProvider, attributeFqn) {
|
|
132
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
133
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
134
|
+
}
|
|
135
|
+
if (!ATTRIBUTE_FQN_RE.test(attributeFqn)) {
|
|
136
|
+
throw new ConfigurationError('invalid attribute FQN format');
|
|
137
|
+
}
|
|
138
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
139
|
+
try {
|
|
140
|
+
await platform.v1.attributes.getAttribute({
|
|
141
|
+
identifier: { case: 'fqn', value: attributeFqn },
|
|
142
|
+
});
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
if (e instanceof ConnectError && e.code === Code.NotFound) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
throw new NetworkError(`[GetAttribute] ${extractRpcErrorMessage(e)}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reports whether the attribute value FQN exists on the platform.
|
|
154
|
+
*
|
|
155
|
+
* `valueFqn` should be a full attribute value FQN (with `/value/` segment):
|
|
156
|
+
* `https://<namespace>/attr/<attribute_name>/value/<value>`
|
|
157
|
+
*
|
|
158
|
+
* @param platformUrl The platform base URL.
|
|
159
|
+
* @param authProvider An auth provider for the request.
|
|
160
|
+
* @param valueFqn The attribute value FQN to check.
|
|
161
|
+
* @returns `true` if the value exists, `false` if it does not.
|
|
162
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
163
|
+
* @throws {@link NetworkError} if a service error occurs.
|
|
164
|
+
*/
|
|
165
|
+
export async function attributeValueExists(platformUrl, authProvider, valueFqn) {
|
|
166
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
167
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
168
|
+
}
|
|
169
|
+
if (!ATTRIBUTE_VALUE_FQN_RE.test(valueFqn)) {
|
|
170
|
+
throw new ConfigurationError('invalid attribute value FQN format');
|
|
171
|
+
}
|
|
172
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
173
|
+
let resp;
|
|
174
|
+
try {
|
|
175
|
+
resp = await platform.v1.attributes.getAttributeValuesByFqns({ fqns: [valueFqn] });
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
throw new NetworkError(`[GetAttributeValuesByFqns] ${extractRpcErrorMessage(e)}`);
|
|
179
|
+
}
|
|
180
|
+
return valueFqn in resp.fqnAttributeValues;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzY292ZXJ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3BvbGljeS9kaXNjb3ZlcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsa0JBQWtCLEVBQUUsWUFBWSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRXhGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN4RSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFHaEQsK0VBQStFO0FBQy9FLGlGQUFpRjtBQUNqRix1RUFBdUU7QUFDdkUsTUFBTSx5QkFBeUIsR0FBRyxFQUFFLENBQUM7QUFFckMsMkVBQTJFO0FBQzNFLHNGQUFzRjtBQUN0RixNQUFNLHlCQUF5QixHQUFHLElBQUksQ0FBQztBQUV2QyxnRkFBZ0Y7QUFDaEYsNkVBQTZFO0FBQzdFLGtFQUFrRTtBQUNsRSxNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQUU5Qiw0RUFBNEU7QUFDNUUsNkVBQTZFO0FBQzdFLE1BQU0sc0JBQXNCLEdBQzFCLG9GQUFvRixDQUFDO0FBRXZGLG9GQUFvRjtBQUNwRiw2RUFBNkU7QUFDN0UsTUFBTSxnQkFBZ0IsR0FBRywwREFBMEQsQ0FBQztBQUVwRjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjLENBQ2xDLFdBQW1CLEVBQ25CLFlBQTBCLEVBQzFCLFNBQWtCO0lBRWxCLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO1FBQ3BDLE1BQU0sSUFBSSxrQkFBa0IsQ0FBQyxxQ0FBcUMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFDRCxNQUFNLFFBQVEsR0FBRyxJQUFJLGNBQWMsQ0FBQyxF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
|
package/package.json
CHANGED
package/src/errors.ts
CHANGED
|
@@ -119,3 +119,12 @@ export class PermissionDeniedError extends TdfError {
|
|
|
119
119
|
export class UnsupportedFeatureError extends TdfError {
|
|
120
120
|
override name = 'UnsupportedFeatureError';
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* One or more attribute value FQNs were not found on the platform.
|
|
125
|
+
* Thrown by {@link validateAttributes} and {@link validateAttributeValue} when the platform
|
|
126
|
+
* does not recognize the requested FQNs.
|
|
127
|
+
*/
|
|
128
|
+
export class AttributeNotFoundError extends TdfError {
|
|
129
|
+
override name = 'AttributeNotFoundError';
|
|
130
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export { type AuthProvider, type HttpMethod, HttpRequest, withHeaders } from './auth/auth.js';
|
|
2
2
|
export * as AuthProviders from './auth/providers.js';
|
|
3
3
|
export { attributeFQNsAsValues } from './policy/api.js';
|
|
4
|
+
export {
|
|
5
|
+
listAttributes,
|
|
6
|
+
validateAttributes,
|
|
7
|
+
attributeExists,
|
|
8
|
+
attributeValueExists,
|
|
9
|
+
} from './policy/discovery.js';
|
|
4
10
|
export { version, clientType, tdfSpecVersion } from './version.js';
|
|
5
11
|
export { PlatformClient, type PlatformClientOptions, type PlatformServices } from './platform.js';
|
|
6
12
|
export * from './opentdf.js';
|
|
@@ -12,6 +18,7 @@ export {
|
|
|
12
18
|
DecryptError,
|
|
13
19
|
NetworkError,
|
|
14
20
|
AttributeValidationError,
|
|
21
|
+
AttributeNotFoundError,
|
|
15
22
|
ConfigurationError,
|
|
16
23
|
} from './errors.js';
|
|
17
24
|
export * from './seekable.js';
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { ConnectError, Code } from '@connectrpc/connect';
|
|
2
|
+
import { AttributeNotFoundError, ConfigurationError, NetworkError } from '../errors.js';
|
|
3
|
+
import { type AuthProvider } from '../auth/auth.js';
|
|
4
|
+
import { extractRpcErrorMessage, validateSecureUrl } from '../utils.js';
|
|
5
|
+
import { PlatformClient } from '../platform.js';
|
|
6
|
+
import type { Attribute } from '../platform/policy/objects_pb.js';
|
|
7
|
+
|
|
8
|
+
// Caps the pagination loop in listAttributes. 10 pages × 1000 records = 10,000
|
|
9
|
+
// attributes maximum, which is generous for browser use while preventing runaway
|
|
10
|
+
// memory growth if a server repeatedly returns a non-zero next_offset.
|
|
11
|
+
const MAX_LIST_ATTRIBUTES_PAGES = 10;
|
|
12
|
+
|
|
13
|
+
// Number of attributes to request per page. Matches the platform's default
|
|
14
|
+
// (ListRequestLimitDefault = 1000) so behavior is stable regardless of server config.
|
|
15
|
+
const LIST_ATTRIBUTES_PAGE_SIZE = 1000;
|
|
16
|
+
|
|
17
|
+
// Matches the server-side proto constraint: GetAttributeValuesByFqnsRequest has
|
|
18
|
+
// max_items: 250 on the fqns field, so the client rejects oversized requests
|
|
19
|
+
// locally instead of receiving a cryptic server validation error.
|
|
20
|
+
const MAX_VALIDATE_FQNS = 250;
|
|
21
|
+
|
|
22
|
+
// Attribute value FQN format: https://<namespace>/attr/<name>/value/<value>
|
|
23
|
+
// Restricts to safe URL characters to prevent XSS via FQNs in error messages
|
|
24
|
+
const ATTRIBUTE_VALUE_FQN_RE =
|
|
25
|
+
/^https?:\/\/[a-zA-Z0-9._~%-]+\/attr\/[a-zA-Z0-9._~%-]+\/value\/[a-zA-Z0-9._~%-]+$/i;
|
|
26
|
+
|
|
27
|
+
// Attribute-level FQN format: https://<namespace>/attr/<name> (no /value/ segment)
|
|
28
|
+
// Restricts to safe URL characters to prevent XSS via FQNs in error messages
|
|
29
|
+
const ATTRIBUTE_FQN_RE = /^https?:\/\/[a-zA-Z0-9._~%-]+\/attr\/[a-zA-Z0-9._~%-]+$/i;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns all active attributes available on the platform, auto-paginating through all results.
|
|
33
|
+
* An optional namespace name or ID may be provided to filter results.
|
|
34
|
+
*
|
|
35
|
+
* Use this before calling `createZTDF()` to see what attributes are available for data tagging.
|
|
36
|
+
*
|
|
37
|
+
* @param platformUrl The platform base URL.
|
|
38
|
+
* @param authProvider An auth provider for the request.
|
|
39
|
+
* @param namespace Optional namespace name or ID to filter results.
|
|
40
|
+
* @returns All active {@link Attribute} objects on the platform.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const attrs = await listAttributes(platformUrl, authProvider);
|
|
45
|
+
* for (const a of attrs) {
|
|
46
|
+
* console.log(a.fqn);
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export async function listAttributes(
|
|
51
|
+
platformUrl: string,
|
|
52
|
+
authProvider: AuthProvider,
|
|
53
|
+
namespace?: string
|
|
54
|
+
): Promise<Attribute[]> {
|
|
55
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
56
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
57
|
+
}
|
|
58
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
59
|
+
const result: Attribute[] = [];
|
|
60
|
+
let nextOffset = 0;
|
|
61
|
+
|
|
62
|
+
for (let pages = 0; pages < MAX_LIST_ATTRIBUTES_PAGES; pages++) {
|
|
63
|
+
let resp;
|
|
64
|
+
try {
|
|
65
|
+
resp = await platform.v1.attributes.listAttributes({
|
|
66
|
+
namespace: namespace ?? '',
|
|
67
|
+
pagination: { offset: nextOffset, limit: LIST_ATTRIBUTES_PAGE_SIZE },
|
|
68
|
+
});
|
|
69
|
+
} catch (e) {
|
|
70
|
+
throw new NetworkError(`[ListAttributes] ${extractRpcErrorMessage(e)}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
result.push(...resp.attributes);
|
|
74
|
+
nextOffset = resp.pagination?.nextOffset ?? 0;
|
|
75
|
+
if (nextOffset === 0) {
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
throw new ConfigurationError(
|
|
81
|
+
`listAttributes returned more than ${MAX_LIST_ATTRIBUTES_PAGES * LIST_ATTRIBUTES_PAGE_SIZE} attributes. Use the namespace parameter to narrow results.`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Checks that all provided attribute value FQNs exist on the platform.
|
|
87
|
+
* Validates FQN format first, then verifies existence via the platform API.
|
|
88
|
+
*
|
|
89
|
+
* Use this before `createZTDF()` to catch missing or misspelled attributes early
|
|
90
|
+
* instead of discovering the problem at decryption time.
|
|
91
|
+
*
|
|
92
|
+
* @param platformUrl The platform base URL.
|
|
93
|
+
* @param authProvider An auth provider for the request.
|
|
94
|
+
* @param fqns Attribute value FQNs to validate, in the form
|
|
95
|
+
* `https://<namespace>/attr/<name>/value/<value>`.
|
|
96
|
+
* @throws {@link AttributeNotFoundError} if any FQNs are not found on the platform.
|
|
97
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or there are too many FQNs.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* await validateAttributes(platformUrl, authProvider, [
|
|
102
|
+
* 'https://opentdf.io/attr/department/value/marketing',
|
|
103
|
+
* ]);
|
|
104
|
+
* // Safe to encrypt — all attributes confirmed present
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export async function validateAttributes(
|
|
108
|
+
platformUrl: string,
|
|
109
|
+
authProvider: AuthProvider,
|
|
110
|
+
fqns: string[]
|
|
111
|
+
): Promise<void> {
|
|
112
|
+
if (!fqns || fqns.length === 0) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
117
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (fqns.length > MAX_VALIDATE_FQNS) {
|
|
121
|
+
throw new ConfigurationError(
|
|
122
|
+
`too many attribute FQNs: ${fqns.length} exceeds maximum of ${MAX_VALIDATE_FQNS}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (const fqn of fqns) {
|
|
127
|
+
if (!ATTRIBUTE_VALUE_FQN_RE.test(fqn)) {
|
|
128
|
+
throw new ConfigurationError('invalid attribute value FQN format');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
133
|
+
let resp;
|
|
134
|
+
try {
|
|
135
|
+
resp = await platform.v1.attributes.getAttributeValuesByFqns({ fqns });
|
|
136
|
+
} catch (e) {
|
|
137
|
+
throw new NetworkError(`[GetAttributeValuesByFqns] ${extractRpcErrorMessage(e)}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const found = resp.fqnAttributeValues;
|
|
141
|
+
const missing = fqns.filter((fqn) => !(fqn in found));
|
|
142
|
+
if (missing.length > 0) {
|
|
143
|
+
throw new AttributeNotFoundError(`attribute not found: ${missing.length} FQN(s) missing`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Reports whether the attribute definition identified by `attributeFqn` exists on the platform.
|
|
149
|
+
*
|
|
150
|
+
* `attributeFqn` should be an attribute-level FQN (no `/value/` segment):
|
|
151
|
+
* `https://<namespace>/attr/<attribute_name>`
|
|
152
|
+
*
|
|
153
|
+
* @param platformUrl The platform base URL.
|
|
154
|
+
* @param authProvider An auth provider for the request.
|
|
155
|
+
* @param attributeFqn The attribute-level FQN to check.
|
|
156
|
+
* @returns `true` if the attribute exists, `false` if it does not.
|
|
157
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
158
|
+
* @throws {@link NetworkError} if a non-not-found service error occurs.
|
|
159
|
+
*/
|
|
160
|
+
export async function attributeExists(
|
|
161
|
+
platformUrl: string,
|
|
162
|
+
authProvider: AuthProvider,
|
|
163
|
+
attributeFqn: string
|
|
164
|
+
): Promise<boolean> {
|
|
165
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
166
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!ATTRIBUTE_FQN_RE.test(attributeFqn)) {
|
|
170
|
+
throw new ConfigurationError('invalid attribute FQN format');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
174
|
+
try {
|
|
175
|
+
await platform.v1.attributes.getAttribute({
|
|
176
|
+
identifier: { case: 'fqn', value: attributeFqn },
|
|
177
|
+
});
|
|
178
|
+
return true;
|
|
179
|
+
} catch (e) {
|
|
180
|
+
if (e instanceof ConnectError && e.code === Code.NotFound) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
throw new NetworkError(`[GetAttribute] ${extractRpcErrorMessage(e)}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Reports whether the attribute value FQN exists on the platform.
|
|
189
|
+
*
|
|
190
|
+
* `valueFqn` should be a full attribute value FQN (with `/value/` segment):
|
|
191
|
+
* `https://<namespace>/attr/<attribute_name>/value/<value>`
|
|
192
|
+
*
|
|
193
|
+
* @param platformUrl The platform base URL.
|
|
194
|
+
* @param authProvider An auth provider for the request.
|
|
195
|
+
* @param valueFqn The attribute value FQN to check.
|
|
196
|
+
* @returns `true` if the value exists, `false` if it does not.
|
|
197
|
+
* @throws {@link ConfigurationError} if the FQN format is invalid or the URL is insecure.
|
|
198
|
+
* @throws {@link NetworkError} if a service error occurs.
|
|
199
|
+
*/
|
|
200
|
+
export async function attributeValueExists(
|
|
201
|
+
platformUrl: string,
|
|
202
|
+
authProvider: AuthProvider,
|
|
203
|
+
valueFqn: string
|
|
204
|
+
): Promise<boolean> {
|
|
205
|
+
if (!validateSecureUrl(platformUrl)) {
|
|
206
|
+
throw new ConfigurationError('platformUrl must use HTTPS protocol');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!ATTRIBUTE_VALUE_FQN_RE.test(valueFqn)) {
|
|
210
|
+
throw new ConfigurationError('invalid attribute value FQN format');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const platform = new PlatformClient({ authProvider, platformUrl });
|
|
214
|
+
let resp;
|
|
215
|
+
try {
|
|
216
|
+
resp = await platform.v1.attributes.getAttributeValuesByFqns({ fqns: [valueFqn] });
|
|
217
|
+
} catch (e) {
|
|
218
|
+
throw new NetworkError(`[GetAttributeValuesByFqns] ${extractRpcErrorMessage(e)}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return valueFqn in resp.fqnAttributeValues;
|
|
222
|
+
}
|