From f15e4501b9571a93e929fbbfede5be75bc92d3dc Mon Sep 17 00:00:00 2001 From: Jan Romann Date: Mon, 11 Dec 2023 23:41:52 +0100 Subject: [PATCH] feat!: simplify credentials callback usage --- example/coaps_readproperty.dart | 8 ++- example/complex_example.dart | 12 ++--- example/example.dart | 9 ++-- example/http_basic_authentication.dart | 9 ++-- lib/src/binding_coap/coap_client.dart | 35 ++++++++----- lib/src/binding_coap/coap_client_factory.dart | 26 +++++++--- lib/src/binding_coap/coap_config.dart | 5 +- lib/src/binding_http/http_client.dart | 18 ++++--- lib/src/binding_http/http_client_factory.dart | 25 ++++++--- lib/src/binding_mqtt/mqtt_client.dart | 16 +++--- lib/src/binding_mqtt/mqtt_client_factory.dart | 23 +++++--- .../protocol_client_factory.dart | 3 +- lib/src/core/security_provider.dart | 52 ------------------- lib/src/core/servient.dart | 12 ++--- test/binding_coap/binding_coap_test.dart | 8 +-- test/binding_http/http_test.dart | 39 ++++++++------ 16 files changed, 147 insertions(+), 153 deletions(-) diff --git a/example/coaps_readproperty.dart b/example/coaps_readproperty.dart index b8ebde44..0d323ce9 100644 --- a/example/coaps_readproperty.dart +++ b/example/coaps_readproperty.dart @@ -30,14 +30,12 @@ PskCredentials? _pskCredentialsCallback( Future main(List args) async { final CoapClientFactory coapClientFactory = CoapClientFactory( - CoapConfig( + coapConfig: const CoapConfig( dtlsCiphers: 'PSK-AES128-CCM8', ), + pskCredentialsCallback: _pskCredentialsCallback, ); - final securityProvider = - ClientSecurityProvider(pskCredentialsCallback: _pskCredentialsCallback); - final servient = Servient(clientSecurityProvider: securityProvider) - ..addClientFactory(coapClientFactory); + final servient = Servient()..addClientFactory(coapClientFactory); final wot = await servient.start(); diff --git a/example/complex_example.dart b/example/complex_example.dart index 507cd45a..5868110a 100644 --- a/example/complex_example.dart +++ b/example/complex_example.dart @@ -112,13 +112,13 @@ Future basicCredentialsCallback( } Future main() async { - final coapConfig = CoapConfig(blocksize: 64); - final CoapClientFactory coapClientFactory = CoapClientFactory(coapConfig); - final HttpClientFactory httpClientFactory = HttpClientFactory(); - final securityProvider = ClientSecurityProvider( - basicCredentialsCallback: basicCredentialsCallback, + const coapConfig = CoapConfig(blocksize: 64); + final CoapClientFactory coapClientFactory = CoapClientFactory( + coapConfig: coapConfig, ); - final servient = Servient(clientSecurityProvider: securityProvider) + final HttpClientFactory httpClientFactory = + HttpClientFactory(basicCredentialsCallback: basicCredentialsCallback); + final servient = Servient() ..addClientFactory(coapClientFactory) ..addClientFactory(httpClientFactory); final wot = await servient.start(); diff --git a/example/example.dart b/example/example.dart index 37aa04eb..f81843b5 100644 --- a/example/example.dart +++ b/example/example.dart @@ -24,13 +24,10 @@ Future basicCredentialsCallback( Future main(List args) async { final CoapClientFactory coapClientFactory = CoapClientFactory(); - final HttpClientFactory httpClientFactory = HttpClientFactory(); + final HttpClientFactory httpClientFactory = + HttpClientFactory(basicCredentialsCallback: basicCredentialsCallback); final MqttClientFactory mqttClientFactory = MqttClientFactory(); - final servient = Servient( - clientSecurityProvider: ClientSecurityProvider( - basicCredentialsCallback: basicCredentialsCallback, - ), - ) + final servient = Servient() ..addClientFactory(coapClientFactory) ..addClientFactory(httpClientFactory) ..addClientFactory(mqttClientFactory); diff --git a/example/http_basic_authentication.dart b/example/http_basic_authentication.dart index 6d586e51..2b787b00 100644 --- a/example/http_basic_authentication.dart +++ b/example/http_basic_authentication.dart @@ -68,12 +68,9 @@ Future basicCredentialsCallback( /// Illustrates the usage of both the basic and the automatic security scheme, /// with a server supporting basic authentication. Future main(List args) async { - final HttpClientFactory httpClientFactory = HttpClientFactory(); - final securityProvider = ClientSecurityProvider( - basicCredentialsCallback: basicCredentialsCallback, - ); - final servient = Servient(clientSecurityProvider: securityProvider) - ..addClientFactory(httpClientFactory); + final HttpClientFactory httpClientFactory = + HttpClientFactory(basicCredentialsCallback: basicCredentialsCallback); + final servient = Servient()..addClientFactory(httpClientFactory); final wot = await servient.start(); final thingDescription = ThingDescription(thingDescriptionJson); diff --git a/lib/src/binding_coap/coap_client.dart b/lib/src/binding_coap/coap_client.dart index e68cfb64..9248023d 100644 --- a/lib/src/binding_coap/coap_client.dart +++ b/lib/src/binding_coap/coap_client.dart @@ -52,11 +52,10 @@ class _InternalCoapConfig extends CoapConfigDefault { coap.PskCredentialsCallback? _createPskCallback( Uri uri, - Form? form, - ClientSecurityProvider? clientSecurityProvider, -) { + Form? form, { + ClientPskCallback? pskCredentialsCallback, +}) { final usesPskScheme = form?.usesPskScheme ?? false; - final pskCredentialsCallback = clientSecurityProvider?.pskCredentialsCallback; if (!usesPskScheme || pskCredentialsCallback == null) { return null; @@ -82,11 +81,19 @@ coap.PskCredentialsCallback? _createPskCallback( /// A [ProtocolClient] for the Constrained Application Protocol (CoAP). final class CoapClient implements ProtocolClient { /// Creates a new [CoapClient] based on an optional [CoapConfig]. - CoapClient([this._coapConfig, this._clientSecurityProvider]); + CoapClient({ + CoapConfig? coapConfig, + ClientPskCallback? pskCredentialsCallback, + AceSecurityCallback? aceSecurityCallback, + }) : _pskCredentialsCallback = pskCredentialsCallback, + _aceSecurityCallback = aceSecurityCallback, + _coapConfig = coapConfig; final CoapConfig? _coapConfig; - final ClientSecurityProvider? _clientSecurityProvider; + final ClientPskCallback? _pskCredentialsCallback; + + final AceSecurityCallback? _aceSecurityCallback; Future _createRequest( coap.RequestMethod requestMethod, @@ -153,9 +160,12 @@ final class CoapClient implements ProtocolClient { }) async { final coapClient = coap.CoapClient( uri, - config: _InternalCoapConfig(_coapConfig ?? CoapConfig()), - pskCredentialsCallback: - _createPskCallback(uri, form, _clientSecurityProvider), + config: _InternalCoapConfig(_coapConfig ?? const CoapConfig()), + pskCredentialsCallback: _createPskCallback( + uri, + form, + pskCredentialsCallback: _pskCredentialsCallback, + ), ); final request = await _createRequest( @@ -169,8 +179,7 @@ final class CoapClient implements ProtocolClient { ); final creationHint = await _obtainAceCreationHintFromForm(form); - final aceCredentialsCallback = - _clientSecurityProvider?.aceCredentialsCallback; + final aceCredentialsCallback = _aceSecurityCallback; final coap.CoapResponse response; @@ -236,7 +245,7 @@ final class CoapClient implements ProtocolClient { final coapClient = coap.CoapClient( creationHintUri, - config: _InternalCoapConfig(_coapConfig ?? CoapConfig()), + config: _InternalCoapConfig(_coapConfig ?? const CoapConfig()), ); final response = await coapClient.send(request); @@ -421,7 +430,7 @@ final class CoapClient implements ProtocolClient { final coapClient = coap.CoapClient( form.resolvedHref, - config: _InternalCoapConfig(_coapConfig ?? CoapConfig()), + config: _InternalCoapConfig(_coapConfig ?? const CoapConfig()), ); if (subprotocol == CoapSubprotocol.observe) { diff --git a/lib/src/binding_coap/coap_client_factory.dart b/lib/src/binding_coap/coap_client_factory.dart index 4a761cd2..7807bee2 100644 --- a/lib/src/binding_coap/coap_client_factory.dart +++ b/lib/src/binding_coap/coap_client_factory.dart @@ -13,24 +13,34 @@ import 'coap_config.dart'; /// A [ProtocolClientFactory] that produces CoAP clients. final class CoapClientFactory implements ProtocolClientFactory { /// Creates a new [CoapClientFactory] based on an optional [CoapConfig]. - CoapClientFactory([this.coapConfig]); - - @override - Set get schemes => {'coap', 'coaps'}; + CoapClientFactory({ + this.coapConfig, + ClientPskCallback? pskCredentialsCallback, + AceSecurityCallback? aceSecurityCallback, + }) : _pskCredentialsCallback = pskCredentialsCallback, + _aceSecurityCallback = aceSecurityCallback; /// The [CoapConfig] used to configure new clients. final CoapConfig? coapConfig; + final ClientPskCallback? _pskCredentialsCallback; + + final AceSecurityCallback? _aceSecurityCallback; + + @override + Set get schemes => {'coap', 'coaps'}; + @override bool destroy() { return true; } @override - ProtocolClient createClient([ - ClientSecurityProvider? clientSecurityProvider, - ]) => - CoapClient(coapConfig, clientSecurityProvider); + ProtocolClient createClient() => CoapClient( + coapConfig: coapConfig, + pskCredentialsCallback: _pskCredentialsCallback, + aceSecurityCallback: _aceSecurityCallback, + ); @override bool init() { diff --git a/lib/src/binding_coap/coap_config.dart b/lib/src/binding_coap/coap_config.dart index 0431c053..081eb891 100644 --- a/lib/src/binding_coap/coap_config.dart +++ b/lib/src/binding_coap/coap_config.dart @@ -6,10 +6,13 @@ import 'dart:typed_data'; +import 'package:meta/meta.dart'; + /// Allows for configuring the behavior of CoAP clients and servers. +@immutable class CoapConfig { /// Creates a new [CoapConfig] object. - CoapConfig({ + const CoapConfig({ this.port = 5683, this.securePort = 5684, this.blocksize, diff --git a/lib/src/binding_http/http_client.dart b/lib/src/binding_http/http_client.dart index ae7445de..6de72d21 100644 --- a/lib/src/binding_http/http_client.dart +++ b/lib/src/binding_http/http_client.dart @@ -46,11 +46,19 @@ const _authorizationHeader = 'Authorization'; /// [`ComboSecurityScheme`]: https://w3c.github.io/wot-thing-description/#combosecurityscheme final class HttpClient implements ProtocolClient { /// Creates a new [HttpClient]. - HttpClient(this._clientSecurityProvider); + HttpClient({ + AsyncClientSecurityCallback? basicCredentialsCallback, + AsyncClientSecurityCallback? bearerCredentialsCallback, + }) : _basicCredentialsCallback = basicCredentialsCallback, + _bearerCredentialsCallback = bearerCredentialsCallback; final _client = Client(); - final ClientSecurityProvider? _clientSecurityProvider; + final AsyncClientSecurityCallback? + _basicCredentialsCallback; + + final AsyncClientSecurityCallback? + _bearerCredentialsCallback; Future _applyCredentialsFromForm(Request request, Form form) async { // TODO(JKRhb): Add DigestSecurity back in @@ -210,8 +218,7 @@ final class HttpClient implements ProtocolClient { Form? form, [ BasicCredentials? invalidCredentials, ]) async { - return _clientSecurityProvider?.basicCredentialsCallback - ?.call(uri, form, invalidCredentials); + return _basicCredentialsCallback?.call(uri, form, invalidCredentials); } Future _getBearerCredentials( @@ -219,8 +226,7 @@ final class HttpClient implements ProtocolClient { Form? form, [ BearerCredentials? invalidCredentials, ]) async { - return _clientSecurityProvider?.bearerCredentialsCallback - ?.call(uri, form, invalidCredentials); + return _bearerCredentialsCallback?.call(uri, form, invalidCredentials); } static Map _getHeadersFromForm(Form form) { diff --git a/lib/src/binding_http/http_client_factory.dart b/lib/src/binding_http/http_client_factory.dart index 834754ab..8f4cb4ee 100644 --- a/lib/src/binding_http/http_client_factory.dart +++ b/lib/src/binding_http/http_client_factory.dart @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: BSD-3-Clause +import '../core/credentials/basic_credentials.dart'; +import '../core/credentials/bearer_credentials.dart'; import '../core/protocol_interfaces/protocol_client.dart'; import '../core/protocol_interfaces/protocol_client_factory.dart'; import '../core/security_provider.dart'; @@ -13,24 +15,31 @@ import 'http_config.dart'; /// A [ProtocolClientFactory] that produces HTTP and HTTPS clients. final class HttpClientFactory implements ProtocolClientFactory { /// Creates a new [HttpClientFactory] based on an optional [HttpConfig]. - HttpClientFactory([this.httpConfig]); + HttpClientFactory({ + AsyncClientSecurityCallback? basicCredentialsCallback, + AsyncClientSecurityCallback? bearerCredentialsCallback, + }) : _basicCredentialsCallback = basicCredentialsCallback, + _bearerCredentialsCallback = bearerCredentialsCallback; + + final AsyncClientSecurityCallback? + _basicCredentialsCallback; + + final AsyncClientSecurityCallback? + _bearerCredentialsCallback; @override Set get schemes => {'http', 'https'}; - /// The [HttpConfig] used to configure new clients. - final HttpConfig? httpConfig; - @override bool destroy() { return true; } @override - ProtocolClient createClient([ - ClientSecurityProvider? clientSecurityProvider, - ]) => - HttpClient(clientSecurityProvider); + ProtocolClient createClient() => HttpClient( + basicCredentialsCallback: _basicCredentialsCallback, + bearerCredentialsCallback: _bearerCredentialsCallback, + ); @override bool init() { diff --git a/lib/src/binding_mqtt/mqtt_client.dart b/lib/src/binding_mqtt/mqtt_client.dart index 6ba4a776..c39dcb99 100644 --- a/lib/src/binding_mqtt/mqtt_client.dart +++ b/lib/src/binding_mqtt/mqtt_client.dart @@ -27,14 +27,16 @@ import 'mqtt_subscription.dart'; /// Currently, only MQTT version 3.1.1 is supported. final class MqttClient implements ProtocolClient { /// Constructor. - MqttClient( - this._clientSecurityProvider, + MqttClient({ MqttConfig? mqttConfig, - ) : _mqttConfig = mqttConfig ?? MqttConfig(); + AsyncClientSecurityCallback? basicCredentialsCallback, + }) : _mqttConfig = mqttConfig ?? MqttConfig(), + _basicCredentialsCallback = basicCredentialsCallback; - final MqttConfig _mqttConfig; + final AsyncClientSecurityCallback? + _basicCredentialsCallback; - final ClientSecurityProvider? _clientSecurityProvider; + final MqttConfig _mqttConfig; Future _obtainCredentials( Uri uri, @@ -50,8 +52,8 @@ final class MqttClient implements ProtocolClient { return null; } - final basicCredentials = _clientSecurityProvider?.basicCredentialsCallback - ?.call(uri, form, invalidCredentials); + final basicCredentials = + _basicCredentialsCallback?.call(uri, form, invalidCredentials); if (basicCredentials != null) { return basicCredentials; diff --git a/lib/src/binding_mqtt/mqtt_client_factory.dart b/lib/src/binding_mqtt/mqtt_client_factory.dart index 10cc0ce0..31e5b40d 100644 --- a/lib/src/binding_mqtt/mqtt_client_factory.dart +++ b/lib/src/binding_mqtt/mqtt_client_factory.dart @@ -4,9 +4,9 @@ // // SPDX-License-Identifier: BSD-3-Clause +import '../core/credentials/basic_credentials.dart'; import '../core/protocol_interfaces/protocol_client.dart'; import '../core/protocol_interfaces/protocol_client_factory.dart'; - import '../core/security_provider.dart'; import 'constants.dart'; import 'mqtt_client.dart'; @@ -14,12 +14,23 @@ import 'mqtt_config.dart'; /// [ProtocolClientFactory] for creating [MqttClient]s. final class MqttClientFactory implements ProtocolClientFactory { - @override - ProtocolClient createClient([ - ClientSecurityProvider? clientSecurityProvider, + /// Instatiates a new [MqttClientFactory]. + MqttClientFactory({ MqttConfig? mqttConfig, - ]) => - MqttClient(clientSecurityProvider, mqttConfig); + AsyncClientSecurityCallback? basicCredentialsCallback, + }) : _mqttConfig = mqttConfig, + _basicCredentialsCallback = basicCredentialsCallback; + + final MqttConfig? _mqttConfig; + + final AsyncClientSecurityCallback? + _basicCredentialsCallback; + + @override + ProtocolClient createClient() => MqttClient( + mqttConfig: _mqttConfig, + basicCredentialsCallback: _basicCredentialsCallback, + ); @override bool destroy() { diff --git a/lib/src/core/protocol_interfaces/protocol_client_factory.dart b/lib/src/core/protocol_interfaces/protocol_client_factory.dart index b495a0f0..a9905d9f 100644 --- a/lib/src/core/protocol_interfaces/protocol_client_factory.dart +++ b/lib/src/core/protocol_interfaces/protocol_client_factory.dart @@ -4,7 +4,6 @@ // // SPDX-License-Identifier: BSD-3-Clause -import '../security_provider.dart'; import 'protocol_client.dart'; /// Base class for a factory that produces [ProtocolClient]s. @@ -24,5 +23,5 @@ abstract interface class ProtocolClientFactory { /// Creates a new [ProtocolClient] with that supports one or more of the given /// [schemes]. - ProtocolClient createClient([ClientSecurityProvider? clientSecurityProvider]); + ProtocolClient createClient(); } diff --git a/lib/src/core/security_provider.dart b/lib/src/core/security_provider.dart index 2e15848f..146a8207 100644 --- a/lib/src/core/security_provider.dart +++ b/lib/src/core/security_provider.dart @@ -8,12 +8,7 @@ import 'package:dcaf/dcaf.dart'; import '../definitions/form.dart'; import 'credentials/ace_credentials.dart'; -import 'credentials/apikey_credentials.dart'; -import 'credentials/basic_credentials.dart'; -import 'credentials/bearer_credentials.dart'; import 'credentials/credentials.dart'; -import 'credentials/digest_credentials.dart'; -import 'credentials/oauth2_credentials.dart'; import 'credentials/psk_credentials.dart'; /// Function signature for a synchronous callback for providing client @@ -66,53 +61,6 @@ typedef AceSecurityCallback = Future Function( typedef AsyncClientSecurityCallback = Future Function(Uri uri, Form? form, T? invalidCredentials); -/// Class for providing callbacks for client [Credentials] at runtime. -/// -/// Accepts either an [AsyncClientSecurityCallback] for each supported type of -/// [Credentials] or – in the case of [PskCredentials] – a (synchronous) -/// [ClientPskCallback]. -/// -/// Note that not all security schemes are implemented yet, therefore not every -/// callback might actually be usable in practice. -class ClientSecurityProvider { - /// Constructor. - ClientSecurityProvider({ - this.pskCredentialsCallback, - this.basicCredentialsCallback, - this.bearerCredentialsCallback, - this.digestCredentialsCallback, - this.apikeyCredentialsCallback, - this.oauth2CredentialsCallback, - this.aceCredentialsCallback, - }); - - /// Asychronous callback for [ApiKeyCredentials]. - final AsyncClientSecurityCallback? - apikeyCredentialsCallback; - - /// Sychronous callback for [PskCredentials]. - final ClientPskCallback? pskCredentialsCallback; - - /// Asychronous callback for [BasicCredentials]. - final AsyncClientSecurityCallback? basicCredentialsCallback; - - /// Asychronous callback for [DigestCredentials]. - final AsyncClientSecurityCallback? - digestCredentialsCallback; - - /// Asychronous callback for [BearerCredentials]. - final AsyncClientSecurityCallback? - bearerCredentialsCallback; - - // TODO(JKRhb): Is this callback actually needed? - /// Asychronous callback for [OAuth2Credentials]. - final AsyncClientSecurityCallback? - oauth2CredentialsCallback; - - /// Asynchronous callback for [AceCredentials]. - final AceSecurityCallback? aceCredentialsCallback; -} - /// Function signature for a synchronous callback retrieving server /// [Credentials] by Thing [id] at runtime. /// diff --git a/lib/src/core/servient.dart b/lib/src/core/servient.dart index ce1d4ddb..5bed3aa5 100644 --- a/lib/src/core/servient.dart +++ b/lib/src/core/servient.dart @@ -40,11 +40,9 @@ class Servient { /// A custom [contentSerdes] can be passed that supports other media types /// than the default ones. Servient({ - ClientSecurityProvider? clientSecurityProvider, ServerSecurityCallback? serverSecurityCallback, ContentSerdes? contentSerdes, }) : contentSerdes = contentSerdes ?? ContentSerdes(), - _clientSecurityProvider = clientSecurityProvider, _serverSecurityCallback = serverSecurityCallback; final List _servers = []; @@ -52,8 +50,6 @@ class Servient { final Map _things = {}; final Map _consumedThings = {}; - final ClientSecurityProvider? _clientSecurityProvider; - final ServerSecurityCallback? _serverSecurityCallback; /// The [ContentSerdes] object that is used for serializing/deserializing. @@ -200,12 +196,14 @@ class Servient { /// Returns the [ProtocolClient] associated with a given [scheme]. ProtocolClient clientFor(String scheme) { - if (hasClientFor(scheme)) { - return _clientFactories[scheme]!.createClient(_clientSecurityProvider); - } else { + final clientFactory = _clientFactories[scheme]; + + if (clientFactory == null) { throw ServientException( 'Servient has no ClientFactory for scheme $scheme', ); } + + return clientFactory.createClient(); } } diff --git a/test/binding_coap/binding_coap_test.dart b/test/binding_coap/binding_coap_test.dart index c0d100a4..54b61636 100644 --- a/test/binding_coap/binding_coap_test.dart +++ b/test/binding_coap/binding_coap_test.dart @@ -35,7 +35,8 @@ void main() { throwsA(const TypeMatcher()), ); - final customServer = CoapServer(CoapConfig(port: 9001, blocksize: 64)); + final customServer = + CoapServer(const CoapConfig(port: 9001, blocksize: 64)); expect(customServer.port, 9001); expect(customServer.preferredBlockSize, 64); @@ -55,8 +56,9 @@ void main() { expect(defaultClientFactory.destroy(), true); - final customClientFactory = - CoapClientFactory(CoapConfig(port: 9001, blocksize: 64)); + final customClientFactory = CoapClientFactory( + coapConfig: const CoapConfig(port: 9001, blocksize: 64), + ); expect(customClientFactory.coapConfig?.port, 9001); expect(customClientFactory.coapConfig?.blocksize, 64); diff --git a/test/binding_http/http_test.dart b/test/binding_http/http_test.dart index 6a878461..175ae8e9 100644 --- a/test/binding_http/http_test.dart +++ b/test/binding_http/http_test.dart @@ -109,27 +109,32 @@ void main() { 'httpbin.org': BasicCredentials(username, password), }; - final Map digestCredentialsStore = { - 'httpbin.org': DigestCredentials(username, password), - }; - final Map bearerCredentialsStore = { 'httpbin.org': BearerCredentials(token), }; - final clientSecurityProvider = ClientSecurityProvider( - basicCredentialsCallback: (uri, form, [invalidCredentials]) async { - return basicCredentialsStore[uri.host]; - }, - digestCredentialsCallback: (uri, form, [invalidCredentials]) async => - digestCredentialsStore[uri.host], - bearerCredentialsCallback: (uri, form, [invalidCredentials]) async => - bearerCredentialsStore[uri.host], - ); - - final servient = - Servient(clientSecurityProvider: clientSecurityProvider) - ..addClientFactory(HttpClientFactory()); + Future basicCredentialsCallback( + Uri uri, + Form? form, [ + BasicCredentials? invalidCredentials, + ]) async { + return basicCredentialsStore[uri.host]; + } + + Future bearerCredentialsCallback( + Uri uri, + Form? form, [ + BearerCredentials? invalidCredentials, + ]) async => + bearerCredentialsStore[uri.host]; + + final servient = Servient() + ..addClientFactory( + HttpClientFactory( + basicCredentialsCallback: basicCredentialsCallback, + bearerCredentialsCallback: bearerCredentialsCallback, + ), + ); final wot = await servient.start(); final consumedThing = await wot.consume(parsedTd);