Skip to content

Commit

Permalink
Merge 495ebf9 into fd89928
Browse files Browse the repository at this point in the history
  • Loading branch information
erossignon committed Oct 19, 2022
2 parents fd89928 + 495ebf9 commit c913bc3
Show file tree
Hide file tree
Showing 32 changed files with 1,605 additions and 1,103 deletions.
214 changes: 50 additions & 164 deletions CTT/ServerNodeOPCUA/ServerNodeOPCUA.ctt.xml

Large diffs are not rendered by default.

1,376 changes: 688 additions & 688 deletions CTT/ServerNodeOPCUA/uaprofiles.xml

Large diffs are not rendered by default.

Expand Up @@ -22,6 +22,7 @@ import * as ec from "node-opcua-basic-types";
import { QualifiedName, LocalizedText } from "node-opcua-data-model";
import { standardUnits } from "node-opcua-data-access";
import { add_eventGeneratorObject } from "node-opcua-address-space/testHelpers";
import { UInt64, Int64 } from "node-opcua-basic-types";

interface RangeOptions {
low: number;
Expand Down Expand Up @@ -1218,7 +1219,7 @@ function add_multi_state_value_discrete_variables(namespaceDemo: Namespace, pare
nodeId: "s=Simulation_DA_MultiStateValueDiscreteType"
});

function _add_multi_state_variable(parentFolder: UAObject, dataType: string) {
function _add_multi_state_variable(parentFolder: UAObject, dataType: string, value: number | UInt64 | Int64, enumValues: Record<string, number>) {
const name = dataType + "MultiStateValueDiscrete";
const nodeId = "s=" + name;

Expand All @@ -1227,23 +1228,25 @@ function add_multi_state_value_discrete_variables(namespaceDemo: Namespace, pare
browseName: name,
nodeId,
dataType,
enumValues: { Red: 0xff0000, Orange: 0xff9933, Green: 0x00ff00, Blue: 0x0000ff },
value: 0xff0000 // Red
enumValues,
value: 0x0000 // Zero
});
}
const enumValueUnsigned = { Zero: 0x00, One: 0x01, Two: 0x02, Three: 0x03, Four: 0x04, Five: 0x05, Six: 0x06, Seven: 0x07 }
const enumValueSigned = { MinusOne: -1, MinusTwo: -2, MinusThree: -3, ...enumValueUnsigned }

const data = [
{ dataType: "Int16", value: -10 },
{ dataType: "UInt16", value: 10 },
{ dataType: "Int32", value: -100 },
{ dataType: "UInt32", value: 100 },
{ dataType: "Int64", value: [0, 0] },
{ dataType: "UInt64", value: [0, 0] },
{ dataType: "Byte", value: 120 },
{ dataType: "SByte", value: -123 }
{ dataType: "Int16", value: -1, enumValue: enumValueSigned },
{ dataType: "UInt16", value: 10, enumValue: enumValueUnsigned },
{ dataType: "Int32", value: -1, enumValue: enumValueSigned },
{ dataType: "UInt32", value: 100, enumValue: enumValueUnsigned },
{ dataType: "Int64", value: [0, 0], enumValue: enumValueUnsigned },
{ dataType: "UInt64", value: [0, 0], enumValue: enumValueSigned },
{ dataType: "Byte", value: 1, enumValue: enumValueUnsigned },
{ dataType: "SByte", value: -1, enumValue: enumValueSigned }
];
data.forEach((e) => {
_add_multi_state_variable(multistateValueDiscreteTypeFolder, e.dataType);
_add_multi_state_variable(multistateValueDiscreteTypeFolder, e.dataType, e.value, e.enumValue);
});
}

Expand Down
Expand Up @@ -52,11 +52,11 @@ import * as semver from "semver";

import { AddressSpacePrivate } from "../../src/address_space_private";
import { NamespacePrivate } from "../../src/namespace_private";
import { NodeSetLoaderOptions } from "../interfaces/nodeset_loader_options";
import { promoteObjectsAndVariables } from "./namespace_post_step";
import { ensureDatatypeExtracted } from "./ensure_datatype_extracted";
import { decodeXmlExtensionObject } from "./decode_xml_extension_object";
import { makeSemverCompatible } from "./make_semver_compatible";
import { NodeSetLoaderOptions } from "../interfaces/nodeset_loader_options";

const doDebug = checkDebugFlag(__filename);
const debugLog = make_debugLog(__filename);
Expand Down
105 changes: 74 additions & 31 deletions packages/node-opcua-address-space/src/ua_variable_impl.ts
Expand Up @@ -348,23 +348,39 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {

this.semantic_version = 0;
}

private checkPermissionAndAccessLevelPrivate(
private checkAccessLevelPrivate(
_context: ISessionContext,
accessLevel: AccessLevelFlag): boolean {
if (this.userAccessLevel === undefined) {
return true;
}
return (this.userAccessLevel & accessLevel) === accessLevel;
}
private checkPermissionPrivate(
context: ISessionContext,
permission: PermissionType,
accessLevel: AccessLevelFlag
) {
): boolean {
if (!context) return true;
assert(context instanceof SessionContext);
if (context.checkPermission) {
assert(context.checkPermission instanceof Function);
if (!(context.checkPermission instanceof Function)) {
errorLog("context checkPermission is not a function");
return false;
}
if (!context.checkPermission(this, permission)) {
return false;
}
}
if (this.userAccessLevel === undefined) {
return true;
return true;
}
private checkPermissionAndAccessLevelPrivate(
context: ISessionContext,
permission: PermissionType,
accessLevel: AccessLevelFlag): boolean {
if (!this.checkPermissionPrivate(context, permission)) {
return false;
}
return (this.userAccessLevel & accessLevel) === accessLevel;
return this.checkAccessLevelPrivate(context, accessLevel);
}

public isReadable(context: ISessionContext): boolean {
Expand All @@ -375,7 +391,10 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
if (!this.isReadable(context)) {
return false;
}
return this.checkPermissionAndAccessLevelPrivate(context, PermissionType.Read, AccessLevelFlag.CurrentRead);
if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
return false;
}
return this.checkAccessLevelPrivate(context, AccessLevelFlag.CurrentRead);
}

public isWritable(context: ISessionContext): boolean {
Expand Down Expand Up @@ -451,9 +470,12 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
if (!this.isReadable(context)) {
return new DataValue({ statusCode: StatusCodes.BadNotReadable });
}
if (!this.isUserReadable(context)) {
if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
}
if (!this.isUserReadable(context)) {
return new DataValue({ statusCode: StatusCodes.BadNotReadable });
}
if (!isValidDataEncoding(dataEncoding)) {
return new DataValue({ statusCode: StatusCodes.BadDataEncodingInvalid });
}
Expand Down Expand Up @@ -484,10 +506,10 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
) {
debugLog(
chalk.red(" Warning: UAVariable#readValue ") +
chalk.cyan(this.browseName.toString()) +
" (" +
chalk.yellow(this.nodeId.toString()) +
") exists but dataValue has not been defined"
chalk.cyan(this.browseName.toString()) +
" (" +
chalk.yellow(this.nodeId.toString()) +
") exists but dataValue has not been defined"
);
}
return dataValue;
Expand Down Expand Up @@ -689,9 +711,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
if (variant.dataType === null || variant.dataType === undefined) {
throw new Error(
"Variant must provide a valid dataType : variant = " +
variant.toString() +
" this.dataType= " +
this.dataType.toString()
variant.toString() +
" this.dataType= " +
this.dataType.toString()
);
}
if (
Expand All @@ -700,9 +722,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
) {
throw new Error(
"Variant must provide a valid Boolean : variant = " +
variant.toString() +
" this.dataType= " +
this.dataType.toString()
variant.toString() +
" this.dataType= " +
this.dataType.toString()
);
}
if (
Expand All @@ -713,9 +735,9 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
) {
throw new Error(
"Variant must provide a valid LocalizedText : variant = " +
variant.toString() +
" this.dataType= " +
this.dataType.toString()
variant.toString() +
" this.dataType= " +
this.dataType.toString()
);
}
}
Expand Down Expand Up @@ -870,8 +892,13 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
if (!this.isWritable(context)) {
return callback!(null, StatusCodes.BadNotWritable);
}
if (!this.checkPermissionPrivate(context, PermissionType.Write)) {
return new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
}


if (!this.isUserWritable(context)) {
return callback!(null, StatusCodes.BadUserAccessDenied);
return callback!(null, StatusCodes.BadWriteNotSupported);
}

// adjust special case
Expand Down Expand Up @@ -997,9 +1024,14 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
if (writeValue.value!.value.dataType !== DataType.Boolean) {
return callback(null, StatusCodes.BadTypeMismatch);
}
if (!this.canUserWriteHistorizingAttribute(context)) {
if (!this.checkPermissionPrivate(context, PermissionType.WriteHistorizing)) {
return callback(null, StatusCodes.BadUserAccessDenied);
}

if (!this.canUserWriteHistorizingAttribute(context)) {
return callback(null, StatusCodes.BadHistoryOperationUnsupported);
}

// if the variable has no historizing in place reject
if (!this.getChildByName("HA Configuration")) {
return callback(null, StatusCodes.BadNotSupported);
Expand Down Expand Up @@ -1227,11 +1259,16 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
innerCallback(null, dataValue);
};
} else if (!this.isUserReadable(context)) {
} else if (!this.checkPermissionPrivate(context, PermissionType.Read)) {
func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
const dataValue = new DataValue({ statusCode: StatusCodes.BadUserAccessDenied });
innerCallback(null, dataValue);
};
} else if (!this.isUserReadable(context)) {
func = (innerCallback: (err: Error | null, dataValue: DataValue) => void) => {
const dataValue = new DataValue({ statusCode: StatusCodes.BadNotReadable });
innerCallback(null, dataValue);
};
} else {
func = typeof this.refreshFunc === "function" ? this.asyncRefresh.bind(this, new Date()) : readImmediate;
}
Expand Down Expand Up @@ -1560,12 +1597,19 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
continuationData: ContinuationData,
callback: CallbackT<HistoryReadResult>
): void {
if (!this.canUserReadHistory(context)) {

if (!this.checkPermissionPrivate(context, PermissionType.ReadHistory)) {
const result = new HistoryReadResult({
statusCode: StatusCodes.BadUserAccessDenied
});
callback(null, result);
}
if (!this.canUserReadHistory(context)) {
const result = new HistoryReadResult({
statusCode: StatusCodes.BadHistoryOperationUnsupported
});
callback(null, result);
}
const result = new HistoryReadResult({
statusCode: StatusCodes.BadHistoryOperationUnsupported
});
Expand Down Expand Up @@ -1634,8 +1678,7 @@ export class UAVariableImpl extends BaseNodeImpl implements UAVariable {
const nbElements = dataValue.value.dimensions.reduce((acc, x) => acc * x, 1);
if (dataValue.value.value.length !== 0 && dataValue.value.value.length !== nbElements) {
throw new Error(
`Internal Error: matrix dimension doesn't match the number of element in the array : ${dataValue.toString()} "\n expecting ${nbElements} elements but got ${
dataValue.value.value.length
`Internal Error: matrix dimension doesn't match the number of element in the array : ${dataValue.toString()} "\n expecting ${nbElements} elements but got ${dataValue.value.value.length
}`
);
}
Expand Down Expand Up @@ -1984,7 +2027,7 @@ function _Variable_bind_with_timestamped_get(
errorLog(
chalk.red(" Bind variable error: "),
" the timestamped_get function must return a DataValue or a Promise<DataValue>" +
"\n value_check.constructor.name ",
"\n value_check.constructor.name ",
dataValue_verify ? dataValue_verify.constructor.name : "null"
);

Expand Down Expand Up @@ -2179,7 +2222,7 @@ export interface UAVariableImplT<T, DT extends DataType> extends UAVariableImpl,
writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, callback: StatusCodeCallback): void;
writeValue(context: ISessionContext, dataValue: DataValueT<T, DT>, indexRange?: NumericRange | null): Promise<StatusCode>;
}
export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl {}
export class UAVariableImplT<T, DT extends DataType> extends UAVariableImpl { }
// x TO DO
// require("./data_access/ua_variable_data_access");
// require("./historical_access/ua_variable_history");
Expand Up @@ -228,7 +228,7 @@ describe("Method#setPermissions & checkPermission", () => {
dataType: "Double",
historizing: true
});
uaVariable.setRolePermissions([{ roleId: WellKnownRoles.Engineer, permissions: PermissionType.WriteHistorizing }]);
uaVariable.setRolePermissions([{ roleId: WellKnownRoles.Engineer, permissions: PermissionType.ReadHistory | PermissionType.WriteHistorizing }]);
const haConfiguration = addressSpace.getOwnNamespace().addObject({
componentOf: uaVariable,
browseName: "HA Configuration"
Expand Down
Expand Up @@ -8,11 +8,7 @@ import { resolveDynamicExtensionObject } from "./resolve_dynamic_extension_objec

export interface PseudoDataValue { value: Variant };

export async function promoteOpaqueStructure(
session: IBasicSession,
dataValues: PseudoDataValue[]
) {

export function extractDataValueToPromote(dataValues: PseudoDataValue[]): PseudoDataValue[] {
// count number of Opaque Structures
const dataValuesToFix = dataValues.filter((dataValue: PseudoDataValue) =>
dataValue.value && dataValue.value.dataType === DataType.ExtensionObject &&
Expand All @@ -25,7 +21,13 @@ export async function promoteOpaqueStructure(
&& dataValue.value.value[0] instanceof OpaqueStructure)
)
);

return dataValuesToFix;
}
export async function promoteOpaqueStructure(
session: IBasicSession,
dataValues: PseudoDataValue[]
) {
const dataValuesToFix = extractDataValueToPromote(dataValues);
if (dataValuesToFix.length === 0) {
return;
}
Expand Down

0 comments on commit c913bc3

Please sign in to comment.