Skip to content

Commit 185c229

Browse files
colmenerokrd-rti
andauthored
Add dynamic permissions example (#739)
* Initial commit: added folder, qos, idl, readme * basic secure application * update copyright notes * generate signed modified permissions document * permissions expire and communication stops * copilot suggestions, changed idl type, improved readme * permissions document renewal * copilot suggestion * fix wrong copyright dates * update schema version of the Governance The file doesn't exist yet, but it should exist once the release is out. Co-authored-by: krd-rti <[email protected]> * added new line at the end of files * readme errors from linter * c++ formatter changes according to linter --------- Co-authored-by: krd-rti <[email protected]>
1 parent eddf7bd commit 185c229

File tree

12 files changed

+783
-5
lines changed

12 files changed

+783
-5
lines changed

examples/connext_secure/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ if(NOT DEFINED CONNEXTDDS_CONNEXT_SECURE_EXAMPLES)
2424
set(CONNEXTDDS_CONNEXT_SECURE_EXAMPLES
2525
"cds"
2626
"certificate_revocation_list"
27+
"dynamic_permissions"
2728
"lightweight"
2829
"whitelist"
2930
)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Example Code: Dynamic Permissions
2+
3+
## Concept
4+
5+
This example showcases how the Security Plugins enforce Permissions Document
6+
expiration, and how the Permissions Document can be renewed to resume
7+
communication.
8+
9+
## Building the Example
10+
11+
Use the following commands to build the example and get the executables that
12+
you can run:
13+
14+
```sh
15+
cd c++11/
16+
mkdir build && cd build
17+
cmake ..
18+
cmake --build .
19+
```
20+
21+
You can optionally pass the
22+
``-DCONNEXTDDS_DIR=<your_connext_installation_directory>``,
23+
``-DOPENSSL_ROOT_DIR=<your_openssl_installation_directory>``,
24+
``-DCONNEXTDDS_ARCH=<your_architecture>``,
25+
``-DCMAKE_BUILD_TYPE=<Debug/Release>``, and
26+
``-DBUILD_SHARED_LIBS=<ON/OFF>`` variables to the cmake configuration step.
27+
28+
After building the example, you will have a publisher Permissions Document that
29+
expires in 1 minute. If you need to re-create it, please remove this file from
30+
your build directory and re-run the ``createExpiringPermissions`` target.
31+
32+
```sh
33+
rm security/ecdsa01/xml/Permissions2_expiring.xml && \
34+
cmake --build . --target createExpiringPermissions
35+
```
36+
37+
## Running the example
38+
39+
Demo is based on a standard rtiddsgen publisher and subscriber example code.
40+
41+
Run a publisher and a subscriber in separate terminal windows.
42+
43+
```sh
44+
./dynamic_permissions_publisher
45+
```
46+
47+
```sh
48+
./dynamic_permissions_subscriber
49+
```
50+
51+
Verify that they communicate and that the subscriber is receiving data.
52+
53+
```sh
54+
# Publisher
55+
Writing ::DynamicPermissions, count 0
56+
Writing ::DynamicPermissions, count 1
57+
# [...]
58+
59+
# Subscriber
60+
::DynamicPermissions subscriber sleeping up to 1 sec...
61+
[value: 0]
62+
::DynamicPermissions subscriber sleeping up to 1 sec...
63+
[value: 1]
64+
::DynamicPermissions subscriber sleeping up to 1 sec...
65+
# [...]
66+
```
67+
68+
Once the Permissions Document of the publisher DomainParticipant expires, you
69+
will see the following error messages:
70+
71+
```sh
72+
# Publisher
73+
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|CHECK STATUS|LC:Security] RTI_Security_PermissionsGrant_isValidTime:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517658","n":"108000"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85264","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"831AB06E.43876C36.FD825600.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_PermissionsGrant_isValidTime"}]}],"m":"now is after not_after of permissions file"}}
74+
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|CHECK STATUS|LC:Security] RTI_Security_AccessControl_validate_status:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517658","n":"192000"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85264","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"831AB06E.43876C36.FD825600.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_AccessControl_validate_status"}]}],"m":"permissions' validity period is invalid."}}
75+
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|CHECK STATUS|LC:Security] PRESParticipant_onSecurityLocalCredentialValidateEvent:FAILED TO VALIDATE | Local permissions credentials.
76+
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|LC:Security] PRESParticipant_onSecurityLocalCredentialEventListener:FAILED TO VALIDATE | Local credentials.
77+
78+
# Subscriber
79+
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] RTI_Security_PermissionsGrant_isValidTime:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517682","n":"984966998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_PermissionsGrant_isValidTime"}]}],"m":"now is after not_after of permissions file"}}
80+
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] RTI_Security_AccessControl_validatePermissionsDocument:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517682","n":"985028998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_AccessControl_validatePermissionsDocument"}]}],"m":"grant has invalid time"}}
81+
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] RTI_Security_AccessControl_validate_remote_permissions:{"DDS:Security:LogTopicV2":{"f":"10","s":"1","t":{"s":"1748517682","n":"985044998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_AccessControl_validate_remote_permissions"}]}],"m":"failed to validate remote permissions"}}
82+
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] DDS_DomainParticipantTrustPlugins_forwardGetAuthenticatedRemoteParticipantSecurityState:FAILED TO VALIDATE | Remote permissions.
83+
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|LC:Security] PRESParticipant_authorizeRemoteParticipant:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517682","n":"985078998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"RTI:Auth"},{"plugin_method":"PRESParticipant_authorizeRemoteParticipant"}]}],"m":"unauthorized remote participant 831ab06e.43876c36.fd825600 denied by local participant ded844b7.87b9550f.b66dd964"}}
84+
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|LC:Security] PRESParticipant_processHandshake:FAILED TO VALIDATE | Failed to authorize remote DP (GUID: 0x831AB06E,0x43876C36,0xFD825600:0x000001C1).
85+
```
86+
87+
Communication will stop.
88+
89+
## Renewing the Permissions Document
90+
91+
This example updates the publisher DomainParticipant's Permissions Document
92+
after 70 samples. At that point, communication with the subscriber will
93+
resume.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#
2+
# (c) 2025 Copyright, Real-Time Innovations, Inc. All rights reserved.
3+
#
4+
# RTI grants Licensee a license to use, modify, compile, and create derivative
5+
# works of the Software. Licensee has the right to distribute object form
6+
# only for use with RTI products. The Software is provided "as is", with no
7+
# warranty of any type, including any warranty for fitness for any purpose.
8+
# RTI is under no obligation to maintain or support the Software. RTI shall
9+
# not be liable for any incidental or consequential damages arising out of the
10+
# use or inability to use the software.
11+
#
12+
cmake_minimum_required(VERSION 3.11)
13+
project(rtiexamples-dynamic-permissions)
14+
list(APPEND CMAKE_MODULE_PATH
15+
"${CMAKE_CURRENT_SOURCE_DIR}/../../../../resources/cmake/Modules"
16+
)
17+
include(ConnextDdsConfigureCmakeUtils)
18+
connextdds_configure_cmake_utils()
19+
20+
find_package(RTIConnextDDS
21+
"7.0.0"
22+
REQUIRED
23+
COMPONENTS
24+
security_plugins
25+
)
26+
27+
if(NOT TARGET RTIConnextDDS::security_plugins)
28+
message(WARNING "RTIConnextDDS::security_plugins component is missing. Skipping example")
29+
return()
30+
endif()
31+
32+
# Include ConnextDdsAddExample.cmake from resources/cmake
33+
include(ConnextDdsAddExample)
34+
35+
connextdds_add_example(
36+
IDL "dynamic_permissions"
37+
LANG "C++11"
38+
)
39+
40+
include (ConnextDdsGenerateSecurityArtifacts)
41+
connextdds_generate_security_artifacts()
42+
43+
# Do a copy of the original subscriber's Permissions Document, but with the
44+
# validity modified so that it expires in 1 minute.
45+
add_custom_command(
46+
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2_expiring.xml"
47+
COMMAND ${CMAKE_COMMAND}
48+
-DINPUT_FILE="${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2.xml"
49+
-DOUTPUT_FILE="${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2_expiring.xml"
50+
-P ${CMAKE_SOURCE_DIR}/modify_permissions.cmake
51+
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2.xml"
52+
)
53+
54+
# Sign the modified Permissions Document
55+
connextdds_openssl_smime_sign(
56+
INPUT "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2_expiring.xml"
57+
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/signed/signed_Permissions2_expiring.p7s"
58+
SIGNER_CERTIFICATE "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/certs/ca_cert.pem"
59+
PRIVATE_KEY_FILE "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/certs/ca_key.pem"
60+
)
61+
62+
# Create a Permissions Document that is about to expire
63+
add_custom_target(createExpiringPermissions
64+
ALL
65+
DEPENDS
66+
dynamic_permissions_securityArtifacts
67+
"${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/signed/signed_Permissions2_expiring.p7s")
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
(c) 2025 Copyright, Real-Time Innovations, Inc. All rights reserved.
4+
RTI grants Licensee a license to use, modify, compile, and create derivative
5+
works of the Software. Licensee has the right to distribute object form only
6+
for use with RTI products. The Software is provided "as is", with no warranty
7+
of any type, including any warranty for fitness for any purpose. RTI is under
8+
no obligation to maintain or support the Software. RTI shall not be liable for
9+
any incidental or consequential damages arising out of the use or inability to
10+
use the software.
11+
-->
12+
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
13+
xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/7.0.0/rti_dds_qos_profiles.xsd">
14+
<qos_library name="dynamic_permissions_Library">
15+
<qos_profile name="subscriber" base_name="BuiltinQosLib::Generic.Security" is_default_qos="true">
16+
<domain_participant_qos>
17+
<property>
18+
<value>
19+
<element>
20+
<name>dds.sec.auth.identity_ca</name>
21+
<value>file:security/ecdsa01/certs/ca_cert.pem</value>
22+
</element>
23+
<element>
24+
<name>dds.sec.auth.identity_certificate</name>
25+
<value>file:security/ecdsa01/certs/peer1_cert.pem</value>
26+
</element>
27+
<element>
28+
<name>dds.sec.auth.private_key</name>
29+
<value>file:security/ecdsa01/certs/peer1_key.pem</value>
30+
</element>
31+
<element>
32+
<name>dds.sec.access.permissions_ca</name>
33+
<value>file:security/ecdsa01/certs/ca_cert.pem</value>
34+
</element>
35+
<element>
36+
<name>dds.sec.access.governance</name>
37+
<value>file:security/ecdsa01/xml/signed/signed_Governance.p7s</value>
38+
</element>
39+
<element>
40+
<name>dds.sec.access.permissions</name>
41+
<value>file:security/ecdsa01/xml/signed/signed_Permissions1.p7s</value>
42+
</element>
43+
</value>
44+
</property>
45+
</domain_participant_qos>
46+
</qos_profile>
47+
<qos_profile name="publisher" base_name="subscriber">
48+
<domain_participant_qos>
49+
<property>
50+
<value>
51+
<element>
52+
<name>dds.sec.auth.identity_certificate</name>
53+
<value>file:security/ecdsa01/certs/peer2_cert.pem</value>
54+
</element>
55+
<element>
56+
<name>dds.sec.auth.private_key</name>
57+
<value>file:security/ecdsa01/certs/peer2_key.pem</value>
58+
</element>
59+
<element>
60+
<name>dds.sec.access.permissions</name>
61+
<value>file:security/ecdsa01/xml/signed/signed_Permissions2_expiring.p7s</value>
62+
</element>
63+
</value>
64+
</property>
65+
</domain_participant_qos>
66+
</qos_profile>
67+
</qos_library>
68+
</dds>
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* (c) Copyright, Real-Time Innovations, 2025. All rights reserved.
3+
* RTI grants Licensee a license to use, modify, compile, and create derivative
4+
* works of the software solely for use with RTI Connext DDS. Licensee may
5+
* redistribute copies of the software provided that all such copies are subject
6+
* to this license. The software is provided "as is", with no warranty of any
7+
* type, including any warranty for fitness for any purpose. RTI is under no
8+
* obligation to maintain or support the software. RTI shall not be liable for
9+
* any incidental or consequential damages arising out of the use or inability
10+
* to use the software.
11+
*/
12+
13+
#ifndef APPLICATION_H
14+
#define APPLICATION_H
15+
16+
#include <iostream>
17+
#include <csignal>
18+
#include <climits>
19+
20+
namespace application {
21+
22+
// Catch control-C and tell application to shut down
23+
bool shutdown_requested = false;
24+
25+
inline void stop_handler(int)
26+
{
27+
shutdown_requested = true;
28+
std::cout << "preparing to shut down..." << std::endl;
29+
}
30+
31+
inline void setup_signal_handlers()
32+
{
33+
signal(SIGINT, stop_handler);
34+
signal(SIGTERM, stop_handler);
35+
}
36+
37+
enum ParseReturn { PARSE_RETURN_OK, PARSE_RETURN_FAILURE, PARSE_RETURN_EXIT };
38+
39+
struct ApplicationArguments {
40+
ParseReturn parse_result;
41+
unsigned int domain_id;
42+
unsigned int sample_count;
43+
NDDS_Config_LogVerbosity verbosity;
44+
};
45+
46+
inline void set_verbosity(ApplicationArguments &arguments, int verbosity)
47+
{
48+
switch (verbosity) {
49+
case 0:
50+
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_SILENT;
51+
break;
52+
case 1:
53+
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_ERROR;
54+
break;
55+
case 2:
56+
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_WARNING;
57+
break;
58+
case 3:
59+
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_STATUS_ALL;
60+
break;
61+
default:
62+
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_ERROR;
63+
break;
64+
}
65+
}
66+
67+
// Parses application arguments for example. Returns whether to exit.
68+
inline void parse_arguments(
69+
ApplicationArguments &arguments,
70+
int argc,
71+
char *argv[])
72+
{
73+
int arg_processing = 1;
74+
bool show_usage = false;
75+
arguments.domain_id = 0;
76+
arguments.sample_count = INT_MAX;
77+
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_ERROR;
78+
arguments.parse_result = PARSE_RETURN_OK;
79+
80+
while (arg_processing < argc) {
81+
if ((argc > arg_processing + 1)
82+
&& (strcmp(argv[arg_processing], "-d") == 0
83+
|| strcmp(argv[arg_processing], "--domain") == 0)) {
84+
arguments.domain_id = atoi(argv[arg_processing + 1]);
85+
arg_processing += 2;
86+
} else if (
87+
(argc > arg_processing + 1)
88+
&& (strcmp(argv[arg_processing], "-s") == 0
89+
|| strcmp(argv[arg_processing], "--sample-count") == 0)) {
90+
arguments.sample_count = atoi(argv[arg_processing + 1]);
91+
arg_processing += 2;
92+
} else if (
93+
(argc > arg_processing + 1)
94+
&& (strcmp(argv[arg_processing], "-v") == 0
95+
|| strcmp(argv[arg_processing], "--verbosity") == 0)) {
96+
set_verbosity(arguments, atoi(argv[arg_processing + 1]));
97+
arg_processing += 2;
98+
} else if (
99+
strcmp(argv[arg_processing], "-h") == 0
100+
|| strcmp(argv[arg_processing], "--help") == 0) {
101+
std::cout << "Example application." << std::endl;
102+
show_usage = true;
103+
arguments.parse_result = PARSE_RETURN_EXIT;
104+
break;
105+
} else {
106+
std::cout << "Bad parameter." << std::endl;
107+
show_usage = true;
108+
arguments.parse_result = PARSE_RETURN_FAILURE;
109+
break;
110+
}
111+
}
112+
if (show_usage) {
113+
std::cout << "Usage:\n"
114+
" -d, --domain <int> Domain ID this "
115+
"application will\n"
116+
" subscribe in. \n"
117+
" Default: 0\n"
118+
" -s, --sample_count <int> Number of samples to "
119+
"receive before\n"
120+
" cleanly shutting down. \n"
121+
" Default: infinite\n"
122+
" -v, --verbosity <int> How much debugging output "
123+
"to show.\n"
124+
" Range: 0-3 \n"
125+
" Default: 1"
126+
<< std::endl;
127+
}
128+
}
129+
130+
} // namespace application
131+
132+
#endif // APPLICATION_H

0 commit comments

Comments
 (0)