UA Server SDK C++ Bundle  1.3.3.206
 All Data Structures Namespaces Functions Variables Typedefs Enumerations Enumerator Groups Pages
User authentication and authorization

OPC UA requires also a user authentication from the Client user in the Server application during the establishment of an application session. The following sections are explaining the configuration options for user identity types and the implementation necessary to apply the user authorization and authentication.

User authentication means that a user identity is verified during the creation of an application Session. If the user is not known or the user has no rights to create a Session, the user is not able to connect to the server.

User authorization means that an authenticated user that has already access to the server and has successfully created a Session is checked for having the right to execute the requrested action like reading or writing data.

User Authentication

There are different aspects for configuration and implementation of user authentication.

A user can be verified using the user management of the operating system. This option avoids an application specific user management but may make assignment of detailed access rights difficult.

A user can be verified using a OPC server specific user data base. This option allows tailoring of the authorization to any features that should be provided by the server but it requires a user data base implementation and an application specific configuration of the users.

A user can be verified using the user management of the underlying system. This is the typical implementation if the underlying system provides a user management and the requirements for the OPC UA interface as externel interface are fulfilled.

Configuration of user identity types in the server

OPC UA supports different user identityp token types like Anonymous, User Password and Certificates. The concept is extensible to other token types. The SDK is prepared to handle the token types Anonymous and User Password but it can be extented to more token types in the future without changig the interfaces of the SDK.

The OPC UA client requests the possible token types with the service call GetEndpoints. The returned EndpointDescriptions are indicating the allowed user identity token types. The SDK requests the supported types by calling the method ServerConfig::getUserIdentityTokenConfig().

If the default XML implementation of the ServerConfig interface is used, the following XML element contains the settings for the user identity token type.

<!--Configuration for supported user identity tokens-->
<UserIdentityTokens>
<!--Enable anonymous logon true/false-->
<EnableAnonymous>true</EnableAnonymous>
<!--Enable user/password logon true/false-->
<EnableUserPw>true</EnableUserPw>
</UserIdentityTokens>
<!--User identity token configuration end-->

User Authorization

If a user is connected, a server must check if a requested action can be performed based on the user rights.

Typical configuration options are

  • General settings if a user is allowed to execute a certain action like reading or writing of data
  • Configuration which parts of the address space can be accessed by a user
  • Configuration of visibility and access for single variables or pieces of information

It makes sense to implement these options based on user roles or user groups where the different options are assigned to groups or to the Anonymous access.

Session object as key to user authentication and authorization in the server

User authentication and authorization is application specific and can not be provided in a generic way by the SDK. But the SDK provides all means necessary to implement the application specific functionality.

The key to this feature is the Session object. It is passed to all actions that request access to the underlying system encapsulated by the server like browse, read, write, call and monitoring of data changes and events. The information necessary to verify if the user is allowed to execute the requested operation needs to be stored in the Session object. Since this information is specific to each implementation, the application needs to provide a class derived from UaSession that stores the necessary information like user name or user role. It is recommended to store the information necessary to check the accesss right instead of storing the original identity.

The SDK provides methods to create the application specific Session class. If the ServerConfig interface is implemented directly, this is done through the method ServerConfig::createSession(). The user logon is done by implementing the method ServerConfig::logonSessionUser().

If the utility classes from the examples are used, the following steps are necessary

  • Register OpcServerCallback for user authentication
  • Implement callback for session creation and user login
  • Implement application specific session class

The registration is done through the method OpcServer::setCallback().

// Register callback for user authentication
pServer->setCallback(new MyOpcServerCallback);

The sample code for the class implementing the callback interface is provided in the following code section.

class MyOpcServerCallback : public OpcServerCallback
{
public:
MyOpcServerCallback(){}
~MyOpcServerCallback(){}
Session* createSession(OpcUa_Int32 sessionID, const UaNodeId &authenticationToken);
UaStatus logonSessionUser(Session* pSession, UaUserIdentityToken* pUserIdentityToken);
};

The implementation of createSession creates the application specific session SessionUserPw.

Session* MyOpcServerCallback::createSession(OpcUa_Int32 sessionID, const UaNodeId &authenticationToken)
{
return new SessionUserPw(sessionID, authenticationToken);
}

The sample code for the application specific session class SessionUserPw is provided in the following two code sections. The sample code shows the logon to the windows user management. Similar code can be used to logon to a application specific user data base or to use user management functions of other operating systems.

class SessionUserPw: public UaSession
{
UA_DISABLE_COPY(SessionUserPw);
protected:
/* Prohibit use of default constructor **/
SessionUserPw();
/* Prohibit use of default destructor **/
virtual ~SessionUserPw();
public:
/* construction */
SessionUserPw(OpcUa_Int32 sessionID, const UaNodeId &authenticationToken);
/* Validates the user identity token and sets the user for a session. */
UaStatus logonSessionUser(UaUserIdentityTokenUserPassword* pUserPwToken);
private:
UaString m_sUserName;
OpcUa_Void* m_UserContextHandle;
};
SessionUserPw::SessionUserPw(OpcUa_Int32 sessionID, const UaNodeId &authenticationToken)
: UaSession(sessionID, authenticationToken)
{
m_UserContextHandle = (OpcUa_Void*)OpcUa_Null;
}
SessionUserPw::~SessionUserPw()
{
if ( m_UserContextHandle != (OpcUa_Void*)OpcUa_Null )
{
CloseHandle((HANDLE)m_UserContextHandle);
m_UserContextHandle = (OpcUa_Void*)OpcUa_Null;
}
}
UaStatus SessionUserPw::logonSessionUser(UaUserIdentityTokenUserPassword* pUserPwToken)
{
UaStatus ret = OpcUa_BadUserAccessDenied;
if ( pUserPwToken == NULL )
{
return OpcUa_BadUserAccessDenied;
}
#ifdef _WIN32
UaString uaUserName;
UaString uaDomain;
int iBSPos = pUserPwToken->sUserName.find("\\");
int iAtPos = pUserPwToken->sUserName.find("@");
if (iBSPos > 0 && iAtPos == -1)
{
for (int i = 0; i < iBSPos; i++) {uaDomain += pUserPwToken->sUserName.at(i).data();}
for (int i = iBSPos + 1; i < pUserPwToken->sUserName.length(); i++) {uaUserName += pUserPwToken->sUserName.at(i).data();}
}
else if (iAtPos > 0 && iBSPos == -1)
{
for (int i = 0; i < iAtPos; i++) {uaUserName += pUserPwToken->sUserName.at(i).data();}
for (int i = iAtPos + 1; i < pUserPwToken->sUserName.length(); i++) {uaDomain += pUserPwToken->sUserName.at(i).data();}
}
else
{
uaUserName = pUserPwToken->sUserName;
}
#ifdef UNICODE
UaByteArray wsUserName = uaUserName.toUtf16();
const LPTSTR lpszUsername = (wchar_t*)(const UaUShort*)wsUserName;
UaByteArray wsDomain = uaDomain.toUtf16();
const LPTSTR lpszDomain = (wsDomain.size() > 0) ? ((wchar_t*)(const UaUShort*)wsDomain) : NULL;
UaByteArray wsPassword = pUserPwToken->sPassword.toUtf16();
const LPTSTR lpszPassword = (wchar_t*)(const UaUShort*)wsPassword;
#else /* UNICODE */
const char *lpszUsername = uaUserName.toUtf8();
const char *lpszDomain = (uaDomain.length() > 0) ? (uaDomain.toUtf8()) : NULL;
const char *lpszPassword = pUserPwToken->sPassword.toUtf8();
#endif /* UNICODE */
// http://msdn2.microsoft.com/en-us/library/Aa378184.aspx
// requires include Windows.h
// requires lib Advapi32.lib
BOOL logOnResult = LogonUser(
lpszUsername,
lpszDomain,
lpszPassword,
LOGON32_LOGON_NETWORK, // DWORD dwLogonType,
LOGON32_PROVIDER_DEFAULT, // DWORD dwLogonProvider,
(HANDLE*)&m_UserContextHandle);
if ( logOnResult == FALSE )
{
ret = OpcUa_BadUserAccessDenied;
}
else
{
ret = OpcUa_Good;
}
#else /* _WIN32 */
ret = OpcUa_BadUserAccessDenied;
#endif /* _WIN32 */
return ret;
}