|
@@ -14,6 +14,38 @@ forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataType *t) {
|
|
|
v->storageType = UA_VARIANT_DATA_NODELETE;
|
|
|
}
|
|
|
|
|
|
+static UA_UInt32
|
|
|
+getUserWriteMask(UA_Server *server, const UA_Session *session, const UA_Node *node) {
|
|
|
+ if(session == &adminSession)
|
|
|
+ return 0xFFFFFFFF; /* the local admin user has all rights */
|
|
|
+ return node->writeMask &
|
|
|
+ server->config.accessControl.getUserRightsMask(&session->sessionId,
|
|
|
+ session->sessionHandle,
|
|
|
+ &node->nodeId);
|
|
|
+}
|
|
|
+
|
|
|
+static UA_Byte
|
|
|
+getUserAccessLevel(UA_Server *server, const UA_Session *session,
|
|
|
+ const UA_VariableNode *node) {
|
|
|
+ if(session == &adminSession)
|
|
|
+ return 0xFF; /* the local admin user has all rights */
|
|
|
+ return node->accessLevel &
|
|
|
+ server->config.accessControl.getUserAccessLevel(&session->sessionId,
|
|
|
+ session->sessionHandle,
|
|
|
+ &node->nodeId);
|
|
|
+}
|
|
|
+
|
|
|
+static UA_Boolean
|
|
|
+getUserExecutable(UA_Server *server, const UA_Session *session,
|
|
|
+ const UA_MethodNode *node) {
|
|
|
+ if(session == &adminSession)
|
|
|
+ return true; /* the local admin user has all rights */
|
|
|
+ return node->executable &
|
|
|
+ server->config.accessControl.getUserExecutable(&session->sessionId,
|
|
|
+ session->sessionHandle,
|
|
|
+ &node->nodeId);
|
|
|
+}
|
|
|
+
|
|
|
/*****************/
|
|
|
/* Type Checking */
|
|
|
/*****************/
|
|
@@ -683,10 +715,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
|
|
|
forceVariantSetScalar(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_USERWRITEMASK: {
|
|
|
- UA_UInt32 userWriteMask = node->writeMask;
|
|
|
- userWriteMask &=
|
|
|
- server->config.accessControl.getUserRightsMask(&session->sessionId,
|
|
|
- session->sessionHandle, &id->nodeId);
|
|
|
+ UA_UInt32 userWriteMask = getUserWriteMask(server, session, node);
|
|
|
UA_Variant_setScalarCopy(&v->value, &userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
break; }
|
|
|
case UA_ATTRIBUTEID_ISABSTRACT:
|
|
@@ -738,10 +767,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_USERACCESSLEVEL: {
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
|
|
|
- UA_Byte userAccessLevel = ((const UA_VariableNode*)node)->accessLevel;
|
|
|
- userAccessLevel &=
|
|
|
- server->config.accessControl.getUserAccessLevel(&session->sessionId,
|
|
|
- session->sessionHandle, &id->nodeId);
|
|
|
+ UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node);
|
|
|
UA_Variant_setScalarCopy(&v->value, &userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
|
|
|
break; }
|
|
|
case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
|
|
@@ -761,10 +787,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_USEREXECUTABLE: {
|
|
|
CHECK_NODECLASS(UA_NODECLASS_METHOD);
|
|
|
- UA_Boolean userExecutable = ((const UA_MethodNode*)node)->executable;
|
|
|
- userExecutable &=
|
|
|
- server->config.accessControl.getUserExecutable(&session->sessionId,
|
|
|
- session->sessionHandle, &id->nodeId);
|
|
|
+ UA_Boolean userExecutable = getUserExecutable(server, session, (const UA_MethodNode*)node);
|
|
|
UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
break; }
|
|
|
default:
|
|
@@ -986,13 +1009,21 @@ __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
|
|
|
break; \
|
|
|
}
|
|
|
|
|
|
+#define CHECK_USERWRITEMASK(BITPOS) \
|
|
|
+ if(!(userWriteMask & (((UA_UInt32)0x01) << BITPOS ))) { \
|
|
|
+ retval = UA_STATUSCODE_BADUSERACCESSDENIED; \
|
|
|
+ break; \
|
|
|
+ }
|
|
|
+
|
|
|
/* This function implements the main part of the write service and operates on a
|
|
|
copy of the node (not in single-threaded mode). */
|
|
|
static UA_StatusCode
|
|
|
CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
|
UA_Node *node, const UA_WriteValue *wvalue) {
|
|
|
const void *value = wvalue->value.value.data;
|
|
|
+ UA_UInt32 userWriteMask = getUserWriteMask(server, session, node);
|
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
+
|
|
|
switch(wvalue->attributeId) {
|
|
|
case UA_ATTRIBUTEID_NODEID:
|
|
|
case UA_ATTRIBUTEID_NODECLASS:
|
|
@@ -1002,66 +1033,88 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
|
retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_BROWSENAME:
|
|
|
+ CHECK_USERWRITEMASK(2);
|
|
|
CHECK_DATATYPE_SCALAR(QUALIFIEDNAME);
|
|
|
UA_QualifiedName_deleteMembers(&node->browseName);
|
|
|
UA_QualifiedName_copy(value, &node->browseName);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_DISPLAYNAME:
|
|
|
+ CHECK_USERWRITEMASK(6);
|
|
|
CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
|
|
|
UA_LocalizedText_deleteMembers(&node->displayName);
|
|
|
UA_LocalizedText_copy(value, &node->displayName);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_DESCRIPTION:
|
|
|
+ CHECK_USERWRITEMASK(5);
|
|
|
CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
|
|
|
UA_LocalizedText_deleteMembers(&node->description);
|
|
|
UA_LocalizedText_copy(value, &node->description);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_WRITEMASK:
|
|
|
+ CHECK_USERWRITEMASK(20);
|
|
|
CHECK_DATATYPE_SCALAR(UINT32);
|
|
|
node->writeMask = *(const UA_UInt32*)value;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_ISABSTRACT:
|
|
|
+ CHECK_USERWRITEMASK(11);
|
|
|
CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
|
retval = writeIsAbstractAttribute(node, *(const UA_Boolean*)value);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_SYMMETRIC:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
|
|
|
+ CHECK_USERWRITEMASK(15);
|
|
|
CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
|
((UA_ReferenceTypeNode*)node)->symmetric = *(const UA_Boolean*)value;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_INVERSENAME:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
|
|
|
+ CHECK_USERWRITEMASK(10);
|
|
|
CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
|
|
|
UA_LocalizedText_deleteMembers(&((UA_ReferenceTypeNode*)node)->inverseName);
|
|
|
UA_LocalizedText_copy(value, &((UA_ReferenceTypeNode*)node)->inverseName);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
|
|
|
+ CHECK_USERWRITEMASK(3);
|
|
|
CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
|
((UA_ViewNode*)node)->containsNoLoops = *(const UA_Boolean*)value;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_EVENTNOTIFIER:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
|
|
|
+ CHECK_USERWRITEMASK(7);
|
|
|
CHECK_DATATYPE_SCALAR(BYTE);
|
|
|
((UA_ViewNode*)node)->eventNotifier = *(const UA_Byte*)value;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_VALUE:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
|
+ if(node->nodeClass == UA_NODECLASS_VARIABLE) {
|
|
|
+ /* The access to a value variable is granted via the AccessLevel Byte */
|
|
|
+ UA_Byte userAccessLevel = getUserAccessLevel(server, session, (const UA_VariableNode*)node);
|
|
|
+ if(!(userAccessLevel & 0x02)) {
|
|
|
+ retval = UA_STATUSCODE_BADUSERACCESSDENIED;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else { /* UA_NODECLASS_VARIABLETYPE */
|
|
|
+ CHECK_USERWRITEMASK(21);
|
|
|
+ }
|
|
|
retval = writeValueAttribute(server, (UA_VariableNode*)node,
|
|
|
&wvalue->value, &wvalue->indexRange);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_DATATYPE:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
|
+ CHECK_USERWRITEMASK(4);
|
|
|
CHECK_DATATYPE_SCALAR(NODEID);
|
|
|
retval = writeDataTypeAttributeWithVT(server, (UA_VariableNode*)node, (const UA_NodeId*)value);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_VALUERANK:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
|
+ CHECK_USERWRITEMASK(19);
|
|
|
CHECK_DATATYPE_SCALAR(INT32);
|
|
|
retval = writeValueRankAttributeWithVT(server, (UA_VariableNode*)node, *(const UA_Int32*)value);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
|
+ CHECK_USERWRITEMASK(1);
|
|
|
CHECK_DATATYPE_ARRAY(UINT32);
|
|
|
retval = writeArrayDimensionsAttribute(server, (UA_VariableNode*)node,
|
|
|
wvalue->value.value.arrayLength,
|
|
@@ -1069,21 +1122,25 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_ACCESSLEVEL:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
|
+ CHECK_USERWRITEMASK(0);
|
|
|
CHECK_DATATYPE_SCALAR(BYTE);
|
|
|
((UA_VariableNode*)node)->accessLevel = *(const UA_Byte*)value;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
|
+ CHECK_USERWRITEMASK(12);
|
|
|
CHECK_DATATYPE_SCALAR(DOUBLE);
|
|
|
((UA_VariableNode*)node)->minimumSamplingInterval = *(const UA_Double*)value;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_HISTORIZING:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
|
+ CHECK_USERWRITEMASK(9);
|
|
|
CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
|
((UA_VariableNode*)node)->historizing = *(const UA_Boolean*)value;
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_EXECUTABLE:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
|
|
|
+ CHECK_USERWRITEMASK(8);
|
|
|
CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
|
((UA_MethodNode*)node)->executable = *(const UA_Boolean*)value;
|
|
|
break;
|