SIENTIAPDE-1973
SIENTIAPDE-1973: Implement per-connector OPC client certificates and remove legacy read engine. This update overhauls certificate management, transitioning from a single platform certificate to individual client certificates defined in the slot configuration. The CertProvider now downloads certificates from MinIO based on certificate_id, applying dynamic security policies (mode, policy) from the slot. The legacy READ_ENGINE=legacy option and OpcManager have been entirely removed, standardizing on the ServerReader with strict slot v2 enforcement. Write command resolution (ServerConfigResolver) now consistently uses connectors joined with opc_certificates. The CERT_BUCKET environment variable is no longer used.
This commit is contained in:
@@ -1,18 +1,92 @@
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
from asyncua import ua
|
||||
from asyncua.crypto.security_policies import (
|
||||
SecurityPolicyAes128Sha256RsaOaep,
|
||||
SecurityPolicyBasic256Sha256,
|
||||
)
|
||||
|
||||
from ingestor.connection.security import SESSION_TIMEOUT_MS, apply_security
|
||||
from ingestor.connection.security import (
|
||||
SESSION_TIMEOUT_MS,
|
||||
UnsupportedSecurityError,
|
||||
apply_security,
|
||||
is_secured,
|
||||
)
|
||||
|
||||
CERTIFICATE = {'certificate_id': 1, 'application_uri': 'urn:sientia:opc-ingestor'}
|
||||
|
||||
|
||||
def _client():
|
||||
client = MagicMock()
|
||||
client.set_security = AsyncMock()
|
||||
return client
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apply_security_sets_sha256_and_channel_lifetime():
|
||||
client = MagicMock()
|
||||
client.set_security = AsyncMock()
|
||||
await apply_security(client, 'urn:x', 'c.pem', 'k.pem', None)
|
||||
async def test_apply_security_uses_mode_and_policy_and_channel_lifetime():
|
||||
client = _client()
|
||||
|
||||
await apply_security(
|
||||
client, {'mode': 'SignAndEncrypt', 'policy': 'Basic256Sha256'}, CERTIFICATE, 'c', 'k'
|
||||
)
|
||||
|
||||
assert client.application_uri == 'urn:sientia:opc-ingestor'
|
||||
assert client.session_timeout == SESSION_TIMEOUT_MS
|
||||
assert client.secure_channel_timeout == int(SESSION_TIMEOUT_MS * 1.25)
|
||||
args, kwargs = client.set_security.call_args
|
||||
from asyncua.crypto.security_policies import SecurityPolicyBasic256Sha256
|
||||
|
||||
assert args[0] is SecurityPolicyBasic256Sha256
|
||||
assert kwargs == {
|
||||
'certificate': 'c',
|
||||
'private_key': 'k',
|
||||
'server_certificate': None,
|
||||
'mode': ua.MessageSecurityMode.SignAndEncrypt,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apply_security_sign_with_another_policy():
|
||||
client = _client()
|
||||
|
||||
await apply_security(
|
||||
client, {'mode': 'Sign', 'policy': 'Aes128Sha256RsaOaep'}, CERTIFICATE, 'c', 'k'
|
||||
)
|
||||
|
||||
args, kwargs = client.set_security.call_args
|
||||
assert args[0] is SecurityPolicyAes128Sha256RsaOaep
|
||||
assert kwargs['mode'] == ua.MessageSecurityMode.Sign
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
'security',
|
||||
[
|
||||
{'mode': 'Encrypt', 'policy': 'Basic256Sha256'},
|
||||
{'mode': 'Sign', 'policy': 'Basic999'},
|
||||
{'mode': 'Sign', 'policy': 'None'},
|
||||
{},
|
||||
],
|
||||
)
|
||||
async def test_apply_security_rejects_unknown_mode_or_policy(security):
|
||||
client = _client()
|
||||
|
||||
with pytest.raises(UnsupportedSecurityError):
|
||||
await apply_security(client, security, CERTIFICATE, 'c', 'k')
|
||||
client.set_security.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('server_cfg', 'secured'),
|
||||
[
|
||||
({'security': {'mode': 'SignAndEncrypt'}, 'certificate': CERTIFICATE}, True),
|
||||
({'security': {'mode': 'Sign'}, 'certificate': CERTIFICATE}, True),
|
||||
# No certificate: no security (D5), whatever the mode
|
||||
({'security': {'mode': 'SignAndEncrypt'}, 'certificate': None}, False),
|
||||
({'security': {'mode': 'SignAndEncrypt'}}, False),
|
||||
# mode None: no security even with a certificate
|
||||
({'security': {'mode': 'None'}, 'certificate': CERTIFICATE}, False),
|
||||
({}, False),
|
||||
],
|
||||
)
|
||||
def test_is_secured(server_cfg, secured):
|
||||
assert is_secured(server_cfg) is secured
|
||||
|
||||
Reference in New Issue
Block a user