Skip to content

Commit 5d76378

Browse files
author
Kai Hudalla
committed
Simplify AmqpAdapterSaslAuthenticatorFactory.
Store collaborators in factory class' fields only. Signed-off-by: Kai Hudalla <[email protected]>
1 parent 70ba80f commit 5d76378

File tree

4 files changed

+61
-88
lines changed

4 files changed

+61
-88
lines changed

adapters/amqp-vertx/src/main/java/org/eclipse/hono/adapter/amqp/AmqpAdapterSaslAuthenticatorFactory.java

+36-85
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.HashMap;
2121
import java.util.Map;
2222
import java.util.Objects;
23+
import java.util.function.Function;
2324
import java.util.function.Supplier;
2425

2526
import javax.net.ssl.SSLPeerUnverifiedException;
@@ -41,7 +42,6 @@
4142
import org.eclipse.hono.service.auth.device.UsernamePasswordCredentials;
4243
import org.eclipse.hono.service.auth.device.X509AuthProvider;
4344
import org.eclipse.hono.service.limiting.ConnectionLimitManager;
44-
import org.eclipse.hono.service.plan.ResourceLimitChecks;
4545
import org.eclipse.hono.tracing.TracingHelper;
4646
import org.eclipse.hono.util.AuthenticationConstants;
4747
import org.eclipse.hono.util.Constants;
@@ -69,20 +69,21 @@
6969
* <p>
7070
* On successful authentication of the device, a {@link Device} reflecting the device's credentials (as obtained from
7171
* the Credentials service) is stored in the attachments record of the {@code ProtonConnection} under key
72-
* {@link AmqpAdapterConstants#KEY_CLIENT_DEVICE}. The credentials supplied by the client is verified against the credentials that
73-
* the credentials service has on record for the device.
72+
* {@link AmqpAdapterConstants#KEY_CLIENT_DEVICE}. The credentials supplied by the client are verified against
73+
* the credentials that the Credentials service has on record for the device.
7474
* <p>
7575
* This factory supports the SASL PLAIN and SASL EXTERNAL mechanisms for authenticating devices.
7676
*/
7777
public class AmqpAdapterSaslAuthenticatorFactory implements ProtonSaslAuthenticatorFactory {
7878

7979
private final ProtocolAdapterProperties config;
8080
private final TenantClientFactory tenantClientFactory;
81-
private final CredentialsClientFactory credentialsClientFactory;
82-
private final Tracer tracer;
8381
private final Supplier<Span> spanFactory;
84-
private final ConnectionLimitManager connectionLimitManager;
85-
private final ResourceLimitChecks resourceLimitChecks;
82+
private final ConnectionLimitManager adapterConnectionLimit;
83+
private final Function<TenantObject, Future<Void>> tenantConnectionLimit;
84+
private final DeviceCertificateValidator certValidator;
85+
private final HonoClientBasedAuthProvider<UsernamePasswordCredentials> usernamePasswordAuthProvider;
86+
private final HonoClientBasedAuthProvider<SubjectDnCredentials> clientCertAuthProvider;
8687

8788
/**
8889
* Creates a new SASL authenticator factory for an authentication provider. If the AMQP adapter supports
@@ -95,10 +96,9 @@ public class AmqpAdapterSaslAuthenticatorFactory implements ProtonSaslAuthentica
9596
* @param tracer The tracer instance.
9697
* @param spanFactory The factory to use for creating and starting an OpenTracing span to
9798
* trace the authentication of the device.
98-
* @param connectionLimitManager The connection limit manager to use to monitor the number of connections.
99-
* @param resourceLimitChecks The resource limit checks instance to check if the maximum number of connections are
100-
* exceeded or not.
101-
*
99+
* @param adapterConnectionLimit The adapter level connection limit to enforce.
100+
* @param tenantConnectionLimit The tenant level connection limit to enforce. The function must return
101+
* a succeeded future if the connection limit has not been reached yet.
102102
* @throws NullPointerException if any of the parameters are null.
103103
*/
104104
public AmqpAdapterSaslAuthenticatorFactory(
@@ -107,69 +107,43 @@ public AmqpAdapterSaslAuthenticatorFactory(
107107
final ProtocolAdapterProperties config,
108108
final Tracer tracer,
109109
final Supplier<Span> spanFactory,
110-
final ConnectionLimitManager connectionLimitManager,
111-
final ResourceLimitChecks resourceLimitChecks) {
110+
final ConnectionLimitManager adapterConnectionLimit,
111+
final Function<TenantObject, Future<Void>> tenantConnectionLimit) {
112+
112113

114+
Objects.requireNonNull(credentialsClientFactory, "Credentials client cannot be null");
115+
Objects.requireNonNull(tracer);
113116
this.tenantClientFactory = Objects.requireNonNull(tenantClientFactory, "Tenant client factory cannot be null");
114-
this.credentialsClientFactory = Objects.requireNonNull(credentialsClientFactory, "Credentials client factory cannot be null");
115117
this.config = Objects.requireNonNull(config, "configuration cannot be null");
116-
this.tracer = Objects.requireNonNull(tracer);
117118
this.spanFactory = Objects.requireNonNull(spanFactory);
118-
this.connectionLimitManager = Objects.requireNonNull(connectionLimitManager);
119-
this.resourceLimitChecks = Objects.requireNonNull(resourceLimitChecks);
119+
this.adapterConnectionLimit = Objects.requireNonNull(adapterConnectionLimit);
120+
this.tenantConnectionLimit = Objects.requireNonNull(tenantConnectionLimit);
121+
this.certValidator = new DeviceCertificateValidator();
122+
this.clientCertAuthProvider = new X509AuthProvider(credentialsClientFactory, config, tracer);
123+
this.usernamePasswordAuthProvider = new UsernamePasswordAuthProvider(credentialsClientFactory, config, tracer);
120124
}
121125

122126
@Override
123127
public ProtonSaslAuthenticator create() {
124-
return new AmqpAdapterSaslAuthenticator(
125-
tenantClientFactory,
126-
credentialsClientFactory,
127-
config,
128-
tracer,
129-
spanFactory.get(),
130-
connectionLimitManager,
131-
resourceLimitChecks);
128+
return new AmqpAdapterSaslAuthenticator(spanFactory.get());
132129
}
133130

134131
/**
135132
* Manage the SASL authentication process for the AMQP adapter.
136133
*/
137-
static final class AmqpAdapterSaslAuthenticator implements ProtonSaslAuthenticator {
134+
final class AmqpAdapterSaslAuthenticator implements ProtonSaslAuthenticator {
138135

139-
private static final Logger LOG = LoggerFactory.getLogger(AmqpAdapterSaslAuthenticator.class);
136+
private final Logger LOG = LoggerFactory.getLogger(getClass());
140137

141-
private final ProtocolAdapterProperties config;
142-
private final TenantClientFactory tenantClientFactory;
143-
private final CredentialsClientFactory credentialsClientFactory;
144-
private final Tracer tracer;
145138
private final Span currentSpan;
146-
private final ConnectionLimitManager connectionLimitManager;
147-
private final ResourceLimitChecks resourceLimitChecks;
148139

149140
private Sasl sasl;
150141
private boolean succeeded;
151142
private ProtonConnection protonConnection;
152143
private Certificate[] peerCertificateChain;
153-
private HonoClientBasedAuthProvider<UsernamePasswordCredentials> usernamePasswordAuthProvider;
154-
private HonoClientBasedAuthProvider<SubjectDnCredentials> clientCertAuthProvider;
155-
private DeviceCertificateValidator certValidator;
156-
157-
AmqpAdapterSaslAuthenticator(
158-
final TenantClientFactory tenantClientFactory,
159-
final CredentialsClientFactory credentialsClientFactory,
160-
final ProtocolAdapterProperties config,
161-
final Tracer tracer,
162-
final Span currentSpan,
163-
final ConnectionLimitManager connectionLimitManager,
164-
final ResourceLimitChecks resourceLimitChecks) {
165-
166-
this.tenantClientFactory = tenantClientFactory;
167-
this.credentialsClientFactory = credentialsClientFactory;
168-
this.config = config;
169-
this.tracer = tracer;
144+
145+
AmqpAdapterSaslAuthenticator(final Span currentSpan) {
170146
this.currentSpan = currentSpan;
171-
this.connectionLimitManager = connectionLimitManager;
172-
this.resourceLimitChecks = resourceLimitChecks;
173147
}
174148

175149
@Override
@@ -200,7 +174,7 @@ public void process(final Handler<Boolean> completionHandler) {
200174
return;
201175
}
202176

203-
if (connectionLimitManager.isLimitExceeded()) {
177+
if (adapterConnectionLimit.isLimitExceeded()) {
204178
LOG.debug("Connection limit exceeded, reject connection request");
205179
sasl.done(SaslOutcome.PN_SASL_TEMP);
206180
completionHandler.handle(Boolean.TRUE);
@@ -272,14 +246,13 @@ private void verifyPlain(final byte[] saslResponse, final Handler<AsyncResult<De
272246
currentSpan.log(items);
273247

274248
final Future<DeviceUser> authenticationTracker = Future.future();
275-
getUsernamePasswordAuthProvider().authenticate(credentials, currentSpan.context(),
249+
usernamePasswordAuthProvider.authenticate(credentials, currentSpan.context(),
276250
authenticationTracker);
277251
authenticationTracker
278252
.compose(user -> getTenantObject(credentials.getTenantId())
279-
.compose(tenant -> CompositeFuture
280-
.all(checkTenantIsEnabled(tenant),
281-
resourceLimitChecks.isConnectionLimitExceeded(tenant))
282-
.map(ok -> user)))
253+
.compose(tenant -> CompositeFuture.all(
254+
checkTenantIsEnabled(tenant),
255+
tenantConnectionLimit.apply(tenant)).map(ok -> user)))
283256
.setHandler(completer);
284257
}
285258
} catch (CredentialException e) {
@@ -309,7 +282,7 @@ private void verifyExternal(final Handler<AsyncResult<DeviceUser>> completer) {
309282
.compose(tenant -> {
310283
try {
311284
final TrustAnchor trustAnchor = tenantTracker.result().getTrustAnchor();
312-
return getValidator().validate(Collections.singletonList(deviceCert), trustAnchor);
285+
return certValidator.validate(Collections.singletonList(deviceCert), trustAnchor);
313286
} catch(final GeneralSecurityException e) {
314287
LOG.debug("cannot retrieve trust anchor of tenant [{}]", tenant.getTenantId(), e);
315288
return Future.failedFuture(new CredentialException("validation of client certificate failed"));
@@ -319,13 +292,12 @@ private void verifyExternal(final Handler<AsyncResult<DeviceUser>> completer) {
319292
final Future<DeviceUser> user = Future.future();
320293
final String tenantId = tenantTracker.result().getTenantId();
321294
final SubjectDnCredentials credentials = SubjectDnCredentials.create(tenantId, deviceCert.getSubjectX500Principal());
322-
getCertificateAuthProvider().authenticate(credentials, currentSpan.context(), user);
295+
clientCertAuthProvider.authenticate(credentials, currentSpan.context(), user);
323296
return user;
324297
})
325-
.compose(user -> CompositeFuture
326-
.all(checkTenantIsEnabled(tenantTracker.result()),
327-
resourceLimitChecks.isConnectionLimitExceeded(tenantTracker.result()))
328-
.map(ok -> user))
298+
.compose(user -> CompositeFuture.all(
299+
checkTenantIsEnabled(tenantTracker.result()),
300+
tenantConnectionLimit.apply(tenantTracker.result())).map(ok -> user))
329301
.setHandler(completer);
330302
}
331303
}
@@ -347,26 +319,5 @@ private Future<TenantObject> checkTenantIsEnabled(final TenantObject tenant) {
347319
return Future.failedFuture(new CredentialException("AMQP adapter is disabled for tenant"));
348320
}
349321
}
350-
351-
private HonoClientBasedAuthProvider<UsernamePasswordCredentials> getUsernamePasswordAuthProvider() {
352-
if (usernamePasswordAuthProvider == null) {
353-
usernamePasswordAuthProvider = new UsernamePasswordAuthProvider(credentialsClientFactory, config, tracer);
354-
}
355-
return usernamePasswordAuthProvider;
356-
}
357-
358-
private HonoClientBasedAuthProvider<SubjectDnCredentials> getCertificateAuthProvider() {
359-
if (clientCertAuthProvider == null) {
360-
clientCertAuthProvider = new X509AuthProvider(credentialsClientFactory, config, tracer);
361-
}
362-
return clientCertAuthProvider;
363-
}
364-
365-
private DeviceCertificateValidator getValidator() {
366-
if (certValidator == null) {
367-
certValidator = new DeviceCertificateValidator();
368-
}
369-
return certValidator;
370-
}
371322
}
372323
}

adapters/amqp-vertx/src/main/java/org/eclipse/hono/adapter/amqp/impl/VertxBasedAmqpProtocolAdapter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ protected void doStart(final Future<Void> startFuture) {
162162
.withTag(Tags.COMPONENT.getKey(), getTypeName())
163163
.start(),
164164
connectionLimitManager,
165-
getResourceLimitChecks());
165+
this::checkConnectionLimit);
166166
}
167167
return Future.succeededFuture();
168168
}).compose(succcess -> {

adapters/mqtt-vertx-base/src/main/java/org/eclipse/hono/adapter/mqtt/AbstractVertxBasedMqttProtocolAdapter.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,9 @@ private Future<Device> handleEndpointConnectionWithAuthentication(final MqttEndp
439439
return authAttempt
440440
.compose(authenticatedDevice -> CompositeFuture.all(
441441
getTenantConfiguration(authenticatedDevice.getTenantId(), currentSpan.context())
442-
.compose(tenantObj -> CompositeFuture
443-
.all(isAdapterEnabled(tenantObj), getResourceLimitChecks().isConnectionLimitExceeded(tenantObj))),
442+
.compose(tenantObj -> CompositeFuture.all(
443+
isAdapterEnabled(tenantObj),
444+
checkConnectionLimit(tenantObj))),
444445
checkDeviceRegistration(authenticatedDevice, currentSpan.context()))
445446
.map(ok -> authenticatedDevice))
446447
.compose(authenticatedDevice -> createLinks(authenticatedDevice, currentSpan))

service-base/src/main/java/org/eclipse/hono/service/AbstractProtocolAdapterBase.java

+21
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,27 @@ protected final Future<TenantObject> isAdapterEnabled(final TenantObject tenantC
485485
}
486486
}
487487

488+
/**
489+
* Checks if this adapter may accept another connection from a device.
490+
* <p>
491+
* This default implementation uses the
492+
* {@link ResourceLimitChecks#isConnectionLimitExceeded(TenantObject)} method
493+
* to verify if the tenant's connection limit has been reached.
494+
*
495+
* @param tenantConfig The tenant to check the connection limit for.
496+
* @return A succeeded future if the connection limit has not been reached yet
497+
* or if the limits could not be checked.
498+
* Otherwise the future will be failed with a {@link ClientErrorException}
499+
* containing the 403 Forbidden status code.
500+
* @throws NullPointerException if tenant is {@code null}.
501+
*/
502+
protected Future<Void> checkConnectionLimit(final TenantObject tenantConfig) {
503+
504+
Objects.requireNonNull(tenantConfig);
505+
return resourceLimitChecks.isConnectionLimitExceeded(tenantConfig)
506+
.map(r -> (Void) null);
507+
}
508+
488509
/**
489510
* Validates a message's target address for consistency with Hono's addressing rules.
490511
*

0 commit comments

Comments
 (0)