Commit d077ee6a07a29c2184ff1bbae3a3a5932bb6aa6a
1 parent
e83064ec
Improved Security Store to support race conditions during registration
Showing
6 changed files
with
80 additions
and
22 deletions
... | ... | @@ -75,13 +75,11 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
75 | 75 | return device; |
76 | 76 | } |
77 | 77 | |
78 | - //TODO: use different endpoints to isolate tests. | |
79 | - @Ignore() | |
80 | 78 | @Test |
81 | 79 | public void testConnectAndObserveTelemetry() throws Exception { |
82 | 80 | createDeviceProfile(TRANSPORT_CONFIGURATION); |
83 | 81 | X509ClientCredentials credentials = new X509ClientCredentials(); |
84 | - credentials.setEndpoint(endpoint+1); | |
82 | + credentials.setEndpoint(endpoint); | |
85 | 83 | Device device = createDevice(credentials); |
86 | 84 | |
87 | 85 | SingleEntityFilter sef = new SingleEntityFilter(); |
... | ... | @@ -99,7 +97,7 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
99 | 97 | wsClient.waitForReply(); |
100 | 98 | |
101 | 99 | wsClient.registerWaitForUpdate(); |
102 | - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint+1); | |
100 | + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); | |
103 | 101 | Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); |
104 | 102 | client.init(security, coapConfig); |
105 | 103 | String msg = wsClient.waitForUpdate(); | ... | ... |
... | ... | @@ -42,8 +42,8 @@ import org.thingsboard.server.common.transport.util.SslUtil; |
42 | 42 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
43 | 43 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
44 | 44 | import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; |
45 | -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; | |
46 | 45 | import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; |
46 | +import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; | |
47 | 47 | |
48 | 48 | import javax.annotation.PostConstruct; |
49 | 49 | import javax.security.auth.x500.X500Principal; |
... | ... | @@ -67,7 +67,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer |
67 | 67 | private final TbLwM2MDtlsSessionStore sessionStorage; |
68 | 68 | private final LwM2MTransportServerConfig config; |
69 | 69 | private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator; |
70 | - private final TbEditableSecurityStore securityStore; | |
70 | + private final TbMainSecurityStore securityStore; | |
71 | 71 | |
72 | 72 | @SuppressWarnings("deprecation") |
73 | 73 | private StaticCertificateVerifier staticCertificateVerifier; |
... | ... | @@ -134,7 +134,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer |
134 | 134 | if (msg.hasDeviceInfo() && deviceProfile != null) { |
135 | 135 | sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg)); |
136 | 136 | try { |
137 | - securityStore.put(securityInfo); | |
137 | + securityStore.putX509(securityInfo); | |
138 | 138 | } catch (NonUniqueSecurityInfoException e) { |
139 | 139 | log.trace("Failed to add security info: {}", securityInfo, e); |
140 | 140 | } | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; |
17 | 17 | |
18 | 18 | import lombok.RequiredArgsConstructor; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.eclipse.leshan.core.SecurityMode; | |
20 | 21 | import org.eclipse.leshan.core.model.ResourceModel; |
21 | 22 | import org.eclipse.leshan.core.node.LwM2mPath; |
22 | 23 | import org.eclipse.leshan.server.registration.Registration; |
... | ... | @@ -30,7 +31,7 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
30 | 31 | import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; |
31 | 32 | import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; |
32 | 33 | import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; |
33 | -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; | |
34 | +import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; | |
34 | 35 | |
35 | 36 | import java.util.Arrays; |
36 | 37 | import java.util.Collection; |
... | ... | @@ -54,7 +55,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
54 | 55 | |
55 | 56 | private final LwM2mTransportContext context; |
56 | 57 | private final LwM2MTransportServerConfig config; |
57 | - private final TbEditableSecurityStore securityStore; | |
58 | + private final TbMainSecurityStore securityStore; | |
58 | 59 | private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); |
59 | 60 | private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); |
60 | 61 | private final Map<UUID, Lwm2mDeviceProfileTransportConfiguration> profiles = new ConcurrentHashMap<>(); |
... | ... | @@ -75,6 +76,9 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
75 | 76 | oldSession = lwM2MClient.getSession(); |
76 | 77 | TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint()); |
77 | 78 | if (securityInfo.getSecurityMode() != null) { |
79 | + if (SecurityMode.X509.equals(securityInfo.getSecurityMode())) { | |
80 | + securityStore.registerX509(registration.getEndpoint(), registration.getId()); | |
81 | + } | |
78 | 82 | if (securityInfo.getDeviceProfile() != null) { |
79 | 83 | profileUpdate(securityInfo.getDeviceProfile()); |
80 | 84 | if (securityInfo.getSecurityInfo() != null) { |
... | ... | @@ -124,7 +128,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
124 | 128 | if (currentRegistration.getId().equals(registration.getId())) { |
125 | 129 | lwM2MClient.setState(LwM2MClientState.UNREGISTERED); |
126 | 130 | lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint()); |
127 | - this.securityStore.remove(lwM2MClient.getEndpoint()); | |
131 | + this.securityStore.remove(lwM2MClient.getEndpoint(), registration.getId()); | |
128 | 132 | UUID profileId = lwM2MClient.getProfileId(); |
129 | 133 | if (profileId != null) { |
130 | 134 | Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst(); | ... | ... |
... | ... | @@ -22,13 +22,22 @@ import org.jetbrains.annotations.Nullable; |
22 | 22 | import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; |
23 | 23 | import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; |
24 | 24 | |
25 | +import java.util.HashSet; | |
26 | +import java.util.Map; | |
27 | +import java.util.Set; | |
28 | +import java.util.concurrent.ConcurrentHashMap; | |
29 | +import java.util.concurrent.ConcurrentMap; | |
30 | +import java.util.concurrent.locks.Lock; | |
31 | +import java.util.concurrent.locks.ReentrantLock; | |
32 | + | |
25 | 33 | import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; |
26 | 34 | |
27 | 35 | @Slf4j |
28 | -public class TbLwM2mSecurityStore implements TbEditableSecurityStore { | |
36 | +public class TbLwM2mSecurityStore implements TbMainSecurityStore { | |
29 | 37 | |
30 | 38 | private final TbEditableSecurityStore securityStore; |
31 | 39 | private final LwM2mCredentialsSecurityInfoValidator validator; |
40 | + private final ConcurrentMap<String, Set<String>> endpointRegistrations = new ConcurrentHashMap<>(); | |
32 | 41 | |
33 | 42 | public TbLwM2mSecurityStore(TbEditableSecurityStore securityStore, LwM2mCredentialsSecurityInfoValidator validator) { |
34 | 43 | this.securityStore = securityStore; |
... | ... | @@ -61,24 +70,42 @@ public class TbLwM2mSecurityStore implements TbEditableSecurityStore { |
61 | 70 | @Nullable |
62 | 71 | public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) { |
63 | 72 | TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, CLIENT); |
64 | - try { | |
65 | - if (securityInfo != null) { | |
73 | + doPut(securityInfo); | |
74 | + return securityInfo != null ? securityInfo.getSecurityInfo() : null; | |
75 | + } | |
76 | + | |
77 | + private void doPut(TbLwM2MSecurityInfo securityInfo) { | |
78 | + if (securityInfo != null) { | |
79 | + try { | |
66 | 80 | securityStore.put(securityInfo); |
81 | + } catch (NonUniqueSecurityInfoException e) { | |
82 | + log.trace("Failed to add security info: {}", securityInfo, e); | |
67 | 83 | } |
68 | - } catch (NonUniqueSecurityInfoException e) { | |
69 | - log.trace("Failed to add security info: {}", securityInfo, e); | |
70 | 84 | } |
71 | - return securityInfo != null ? securityInfo.getSecurityInfo() : null; | |
72 | 85 | } |
73 | 86 | |
74 | 87 | @Override |
75 | - public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException { | |
76 | - securityStore.put(tbSecurityInfo); | |
88 | + public void putX509(TbLwM2MSecurityInfo securityInfo) throws NonUniqueSecurityInfoException { | |
89 | + securityStore.put(securityInfo); | |
77 | 90 | } |
78 | 91 | |
79 | 92 | @Override |
80 | - public void remove(String endpoint) { | |
81 | - //TODO: Make sure we delay removal of security store from endpoint due to reg/unreg race condition. | |
82 | -// securityStore.remove(endpoint); | |
93 | + public void registerX509(String endpoint, String registrationId) { | |
94 | + endpointRegistrations.computeIfAbsent(endpoint, ep -> new HashSet<>()).add(registrationId); | |
95 | + } | |
96 | + | |
97 | + @Override | |
98 | + public void remove(String endpoint, String registrationId) { | |
99 | + Set<String> epRegistrationIds = endpointRegistrations.get(endpoint); | |
100 | + boolean shouldRemove; | |
101 | + if (epRegistrationIds == null) { | |
102 | + shouldRemove = true; | |
103 | + } else { | |
104 | + epRegistrationIds.remove(registrationId); | |
105 | + shouldRemove = epRegistrationIds.isEmpty(); | |
106 | + } | |
107 | + if (shouldRemove) { | |
108 | + securityStore.remove(endpoint); | |
109 | + } | |
83 | 110 | } |
84 | 111 | } | ... | ... |
... | ... | @@ -51,7 +51,7 @@ public class TbLwM2mStoreFactory { |
51 | 51 | } |
52 | 52 | |
53 | 53 | @Bean |
54 | - private TbEditableSecurityStore securityStore() { | |
54 | + private TbMainSecurityStore securityStore() { | |
55 | 55 | return new TbLwM2mSecurityStore(redisConfiguration.isPresent() && useRedis ? |
56 | 56 | new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new TbInMemorySecurityStore(), validator); |
57 | 57 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.transport.lwm2m.server.store; | |
17 | + | |
18 | +import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; | |
19 | +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; | |
20 | + | |
21 | +public interface TbMainSecurityStore extends TbSecurityStore { | |
22 | + | |
23 | + void putX509(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException; | |
24 | + | |
25 | + void registerX509(String endpoint, String registrationId); | |
26 | + | |
27 | + void remove(String endpoint, String registrationId); | |
28 | + | |
29 | +} | ... | ... |