@nucypher/taco
Version:
### [`nucypher/taco-web`](../../README.md)
182 lines • 8.52 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConditionContext = exports.RESERVED_CONTEXT_PARAMS = void 0;
const taco_auth_1 = require("@nucypher/taco-auth");
const types_1 = require("../../types");
const utils_1 = require("../../utils");
const condition_expr_1 = require("../condition-expr");
const const_1 = require("../const");
const ERR_RESERVED_PARAM = (key) => `Cannot use reserved parameter name ${key} as custom parameter`;
const ERR_INVALID_CUSTOM_PARAM = (key) => `Custom parameter ${key} must start with ${const_1.CONTEXT_PARAM_PREFIX}`;
const ERR_AUTH_PROVIDER_REQUIRED = (key) => `No matching authentication provider to satisfy ${key} context variable in condition`;
const ERR_MISSING_CONTEXT_PARAMS = (params) => `Missing custom context parameter(s): ${params.join(', ')}`;
const ERR_UNKNOWN_CUSTOM_CONTEXT_PARAM = (param) => `Unknown custom context parameter: ${param}`;
const ERR_INVALID_AUTH_PROVIDER_TYPE = (param, expected) => `Invalid AuthProvider type for ${param}; expected ${expected}`;
const ERR_AUTH_PROVIDER_NOT_NEEDED_FOR_CONTEXT_PARAM = (param) => `AuthProvider not necessary for context parameter: ${param}`;
const EXPECTED_AUTH_PROVIDER_TYPES = {
[taco_auth_1.USER_ADDRESS_PARAM_DEFAULT]: [
taco_auth_1.EIP4361AuthProvider,
taco_auth_1.EIP1271AuthProvider,
taco_auth_1.SingleSignOnEIP4361AuthProvider,
],
};
exports.RESERVED_CONTEXT_PARAMS = [taco_auth_1.USER_ADDRESS_PARAM_DEFAULT];
class ConditionContext {
requestedContextParameters;
customContextParameters = {};
authProviders = {};
constructor(condition) {
const condProps = condition.toObj();
ConditionContext.validateCoreConditions(condProps);
this.requestedContextParameters =
ConditionContext.findContextParameters(condProps);
}
static validateCoreConditions(condObject) {
// Checking whether the condition is compatible with the current version of the library
// Intentionally ignoring the return value of the function
new types_1.CoreConditions((0, utils_1.toJSON)(condObject));
}
validateNoMissingContextParameters(parameters) {
// Ok, so at this point we should have all the parameters we need
// If we don't, we have a problem and we should throw
const missingParameters = Array.from(this.requestedContextParameters).filter((key) => parameters[key] === undefined);
if (missingParameters.length > 0) {
throw new Error(ERR_MISSING_CONTEXT_PARAMS(missingParameters));
}
}
async fillContextParameters(requestedContextParameters) {
const parameters = await this.fillAuthContextParameters(requestedContextParameters);
for (const key in this.customContextParameters) {
parameters[key] = this.customContextParameters[key];
}
return parameters;
}
validateAuthProviders() {
for (const param of this.requestedContextParameters) {
// If it's not a user address parameter, we can skip
if (!const_1.USER_ADDRESS_PARAMS.includes(param)) {
continue;
}
// we don't have a corresponding auth provider, we have a problem
if (!this.authProviders[param]) {
throw new Error(ERR_AUTH_PROVIDER_REQUIRED(param));
}
}
}
async fillAuthContextParameters(requestedParameters) {
const entries = await Promise.all([...requestedParameters]
.filter((param) => const_1.USER_ADDRESS_PARAMS.includes(param))
.map(async (param) => {
const maybeAuthProvider = this.authProviders[param];
// TODO: Throw here instead of validating in the constructor?
// TODO: Hide getOrCreateAuthSignature behind a more generic interface
return [param, await maybeAuthProvider.getOrCreateAuthSignature()];
}));
return Object.fromEntries(entries);
}
validateCustomContextParameter(customParam) {
if (!ConditionContext.isContextParameter(customParam)) {
throw new Error(ERR_INVALID_CUSTOM_PARAM(customParam));
}
if (exports.RESERVED_CONTEXT_PARAMS.includes(customParam)) {
throw new Error(ERR_RESERVED_PARAM(customParam));
}
if (!this.requestedContextParameters.has(customParam)) {
throw new Error(ERR_UNKNOWN_CUSTOM_CONTEXT_PARAM(customParam));
}
}
static isContextParameter(param) {
return !!String(param).match(const_1.CONTEXT_PARAM_FULL_MATCH_REGEXP);
}
static findContextParameter(value) {
const includedContextVars = new Set();
// value not set
if (!value) {
return includedContextVars;
}
if (typeof value === 'string') {
if (this.isContextParameter(value)) {
// entire string is context parameter
includedContextVars.add(String(value));
}
else {
// context var could be substring; find all matches
const contextVarMatches = value.match(
// RegExp with 'g' is stateful, so new instance needed every time
new RegExp(const_1.CONTEXT_PARAM_REGEXP.source, 'g'));
if (contextVarMatches) {
for (const match of contextVarMatches) {
includedContextVars.add(match);
}
}
}
}
else if (Array.isArray(value)) {
// array
value.forEach((subValue) => {
const contextVarsForValue = this.findContextParameter(subValue);
contextVarsForValue.forEach((contextVar) => {
includedContextVars.add(contextVar);
});
});
}
else if (typeof value === 'object') {
// dictionary (Record<string, T> - complex object eg. Condition, ConditionVariable, ReturnValueTest etc.)
for (const [, entry] of Object.entries(value)) {
const contextVarsForValue = this.findContextParameter(entry);
contextVarsForValue.forEach((contextVar) => {
includedContextVars.add(contextVar);
});
}
}
return includedContextVars;
}
static findContextParameters(condition) {
// find all the context variables we need
const requestedParameters = new Set();
// iterate through all properties in ConditionProps
const properties = Object.keys(condition);
properties.forEach((prop) => {
this.findContextParameter(condition[prop]).forEach((contextVar) => {
requestedParameters.add(contextVar);
});
});
return requestedParameters;
}
addCustomContextParameterValues(customContextParameters) {
Object.keys(customContextParameters).forEach((key) => {
this.validateCustomContextParameter(key);
this.customContextParameters[key] = customContextParameters[key];
});
}
addAuthProvider(contextParam, authProvider) {
if (!(contextParam in EXPECTED_AUTH_PROVIDER_TYPES)) {
throw new Error(ERR_AUTH_PROVIDER_NOT_NEEDED_FOR_CONTEXT_PARAM(contextParam));
}
const expectedTypes = EXPECTED_AUTH_PROVIDER_TYPES[contextParam];
if (!expectedTypes.some((type) => authProvider instanceof type)) {
throw new Error(ERR_INVALID_AUTH_PROVIDER_TYPE(contextParam, typeof authProvider));
}
this.authProviders[contextParam] = authProvider;
}
async toJson() {
const parameters = await this.toContextParameters();
return (0, utils_1.toJSON)(parameters);
}
async toCoreContext() {
const asJson = await this.toJson();
return new types_1.CoreContext(asJson);
}
toContextParameters = async () => {
this.validateAuthProviders();
const parameters = await this.fillContextParameters(this.requestedContextParameters);
this.validateNoMissingContextParameters(parameters);
return parameters;
};
static fromMessageKit(messageKit) {
const conditionExpr = condition_expr_1.ConditionExpression.fromCoreConditions(messageKit.acp.conditions);
return new ConditionContext(conditionExpr.condition);
}
}
exports.ConditionContext = ConditionContext;
//# sourceMappingURL=context.js.map