High Performance OPC UA Server SDK  1.3.1.248
Authorization

Overview

Warning
The Authorization is currently standardized in a way that differs from this implementation. A future version of the SDK may adopt the new standard and break with the mechanisms described on this page.

The server allows to control access of different users to the addressspace and values of the server. For the authorization to work the Authentication must also be enabled. Authorization is implemented as backend, where the following are currently available:

Null Backend
Disable authorization
Inode Backend
User, group and other permissions for every node

To activate a backend, the according backend must be selected using the cmake option UA_AUTHORIZATION_BACKEND.

Null Backend

Disables Authorization completely and grants every user access to all node.

Inode Backend

The inode is embedded in every node of the addressspace and contains a user id, a group id and the permissions for the user, group and everybody else (other).

Adding Users

Users can be loaded from a users file with the following format:

<userid>:<username>
0:anonymous
1:john
2:root

Users can also be added inside the code:

#include <uaserver/session/authorization/inode/inode.h>
int ret;
ret = ua_inode_add_user("anonymous", 0);
if (ret != 0) goto error;
ret = ua_inode_add_user("john", 1);
if (ret != 0) goto error;
ret = ua_inode_add_user("root", 2);
if (ret != 0) goto error;
Note
The username anonymous has a special meaning and is assigned to an unauthenticated user if anonymous authentication is allowed.

Adding Groups

Groups can be loaded from a groups file with the following format:

<groupid>:<groupname>:<username1>,<username2>
1:operators:root,john

Groups can also be added inside the code:

#include <uaserver/session/authorization/inode/inode.h>
int ret;
ret = ua_inode_add_group("operators", 1);
if (ret != 0) goto error;
ret = ua_inode_add_user_to_group("root", "operators");
if (ret != 0) goto error;
ret = ua_inode_add_user_to_group("john", "operators");
if (ret != 0) goto error;

Access Permissions

The following table shows the access permissions available in the SDK and the nodeclasses these are applicable to:

Permission Nodeclass Description
ATTRREADABLE All All attributes except the value are readable, nodes without this permission will also not show up in browse results
ATTRWRITABLE All All attributes except the value are writable
EVENTREADABLE Object The Events the node creates are readable
EXECUTABLE Method The function associated to the node can be executed
READABLE Variable The value attribute is readable
WRITABLE Variable The value attribute is writable
HISTORYREADABLE Variable The history of the value attribute is readable
HISTORYINSERT Variable New values can be inserted in the history of the value attribute
HISTORYMODIFY Variable Values in the history of the value attribute can be modified
HISTORYDELETE Variable Values from the history of the value attribute can be deleted
Note
To minimize the memory footprint of the server the inode backend shares one bit for HISTORYINSERT, HISTORYMODIFY and HISTORYDELETE. If one of these permissions is granted, the other two are also enabled automatically.

There are also some composite permissions defined by the SDK:

Permission Description
OBSERVATION Read everything: attributes, values, events, history
OPERATION Observation plus write values and execute methods
ALL All of the available permissions

All of these permission can be set independently for the user, the group and others. To set the access permissions there is a define for every combination, e.g.:

  • ATTRREADABLE for others: UA_OTHER_ATTRREADABLE
  • WRITABLE for group: UA_GROUP_WRITABLE
  • OPERATION for user: UA_USER_OPERATION

There is furthermore a special permission UA_ALL_ENCRYPTION_REQUIRED. It ensures the node can only be accessed using an encrypted connection. If this permission is set, it is in effect for user, group and others and is meant to protect confidential nodes.

Setting Access Permissions

Access permissions can be set as default permissions for every newly created node after this call:

#include <uaserver/session/authorization/inode/inode.h>
int ret;
/* Get user and group id */
ret = ua_inode_get_uid("john", &uid);
if (ret != 0) goto error;
ret = ua_inode_get_gid("operators", &gid);
if (ret != 0) goto error;
/* Set default permissions:
* -Everybody can read
* -User john can write values
* -Members of group operators can execute methods
*/
ret = ua_inode_set_default_perm(uid, gid, UA_OTHER_OBSERVATION | UA_USER_WRITABLE | UA_GROUP_EXECUTABLE | );
if (ret != 0) goto error;

Access permissions can also be set for a single node:

#include <uaserver/session/authorization/inode/inode.h>
#include <uaserver/addressspace.h>
int ret;
ua_node_t node;
uint16_t mynsindex = ...
ua_uid_t uid;
/* Get the node to set permissions */
node = ua_node_find_string(mynsindex, "confidential_node");
if (node == UA_NODE_INVALID) goto error;
/* Get user id */
ret = ua_inode_get_uid("root", &uid);
if (ret != 0) goto error;
/* User root has full access to the node, but only
* with an encrypted connection. Everybody else has
* no permission at all.
*/
ret = ua_inode_set_perm(node, uid, 0, UA_USER_ALL | UA_ALL_ENCRYPTION_REQUIRED);
if (ret != 0) goto error;

Checking Access Permissions

When implementing a provider, the provider has to check the permissions before providing information to the client. This works independent of the authorization backend using the following functions:

Example when implementing a provider to read the value attribute (other attributes are handled by the SDK in this example):

#include <uabase/service/readresponse.h>
#include <uabase/service/readrequest.h>
#include <uabase/statuscodes.h>
#include <uabase/attributeid.h>
#include <uaserver/read_internal.h>
#include <uaserver/addressspace.h>
#include <uaprovider/provider.h>
extern uint16_t g_mynsindex;
void myprovider_read(struct uaprovider_read_ctx *ctx)
{
struct ua_readrequest *req = ctx->req;
struct ua_readresponse *res = ctx->res;
ua_node_t node;
int32_t i;
for (i = 0; i < req->num_nodes; i++) {
/* skip node from other namespace */
if (req->nodes[i].node_id.nsindex != g_mynsindex) continue;
/* find the node handle */
node = ua_node_find(&req->nodes[i].node_id);
if (node == UA_NODE_INVALID) continue;
if (req->nodes[i].attribute_id == UA_ATTRIBUTEID_VALUE) {
#ifdef UA_AUTHORIZATION_SUPPORT
res->results[i].status = ua_authorization_is_readable(node, &ctx->session->user_ctx);
if (res->results[i].status != 0) continue;
#endif
/* read value */
...
} else {
/* read internal checks the access permissions itself */
uaserver_read_internal(node, ctx->session, req->max_age, req->ts, req->nodes[i].attribute_id, NULL, 0, &res->results[i]);
}
}
}