Commit 38af4d5d2da99804c2aebe829e9c6aeb9b786701
1 parent
7322afac
Implementation of custom L2M2M Authorizer
Showing
8 changed files
with
149 additions
and
15 deletions
@@ -15,14 +15,53 @@ | @@ -15,14 +15,53 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.lwm2m.secure; | 16 | package org.thingsboard.server.transport.lwm2m.secure; |
17 | 17 | ||
18 | +import lombok.RequiredArgsConstructor; | ||
18 | import org.eclipse.leshan.core.request.Identity; | 19 | import org.eclipse.leshan.core.request.Identity; |
19 | import org.eclipse.leshan.core.request.UplinkRequest; | 20 | import org.eclipse.leshan.core.request.UplinkRequest; |
20 | import org.eclipse.leshan.server.registration.Registration; | 21 | import org.eclipse.leshan.server.registration.Registration; |
21 | import org.eclipse.leshan.server.security.Authorizer; | 22 | import org.eclipse.leshan.server.security.Authorizer; |
23 | +import org.eclipse.leshan.server.security.SecurityChecker; | ||
24 | +import org.eclipse.leshan.server.security.SecurityInfo; | ||
25 | +import org.springframework.stereotype.Component; | ||
26 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | ||
27 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | ||
28 | +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; | ||
29 | +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore; | ||
22 | 30 | ||
31 | +@Component | ||
32 | +@RequiredArgsConstructor | ||
33 | +@TbLwM2mTransportComponent | ||
23 | public class TbLwM2MAuthorizer implements Authorizer { | 34 | public class TbLwM2MAuthorizer implements Authorizer { |
35 | + | ||
36 | + private final TbLwM2MDtlsSessionStore sessionStorage; | ||
37 | + private final TbLwM2mSecurityStore securityStore; | ||
38 | + private final SecurityChecker securityChecker = new SecurityChecker(); | ||
39 | + private final LwM2mClientContext clientContext; | ||
40 | + | ||
24 | @Override | 41 | @Override |
25 | public Registration isAuthorized(UplinkRequest<?> request, Registration registration, Identity senderIdentity) { | 42 | public Registration isAuthorized(UplinkRequest<?> request, Registration registration, Identity senderIdentity) { |
26 | - return null; | 43 | + if (senderIdentity.isX509()) { |
44 | + TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint()); | ||
45 | + if (sessionInfo != null) { | ||
46 | + if (senderIdentity.getX509CommonName().equals(sessionInfo.getX509CommonName())) { | ||
47 | + clientContext.registerClient(registration, sessionInfo.getCredentials()); | ||
48 | + // X509 certificate is valid and matches endpoint. | ||
49 | + return registration; | ||
50 | + } else { | ||
51 | + // X509 certificate is not valid. | ||
52 | + return null; | ||
53 | + } | ||
54 | + } | ||
55 | + // If session info is not found, this may be the trusted certificate, so we still need to check all other options below. | ||
56 | + } | ||
57 | + SecurityInfo expectedSecurityInfo = null; | ||
58 | + if (securityStore != null) { | ||
59 | + expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint()); | ||
60 | + } | ||
61 | + if (securityChecker.checkSecurityInfo(registration.getEndpoint(), senderIdentity, expectedSecurityInfo)) { | ||
62 | + return registration; | ||
63 | + } else { | ||
64 | + return null; | ||
65 | + } | ||
27 | } | 66 | } |
28 | } | 67 | } |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.lwm2m.secure; | 16 | package org.thingsboard.server.transport.lwm2m.secure; |
17 | 17 | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
18 | import lombok.RequiredArgsConstructor; | 19 | import lombok.RequiredArgsConstructor; |
19 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
20 | import org.eclipse.californium.elements.util.CertPathUtil; | 21 | import org.eclipse.californium.elements.util.CertPathUtil; |
@@ -32,6 +33,7 @@ import org.eclipse.californium.scandium.util.ServerNames; | @@ -32,6 +33,7 @@ import org.eclipse.californium.scandium.util.ServerNames; | ||
32 | import org.springframework.beans.factory.annotation.Value; | 33 | import org.springframework.beans.factory.annotation.Value; |
33 | import org.springframework.stereotype.Component; | 34 | import org.springframework.stereotype.Component; |
34 | import org.springframework.util.StringUtils; | 35 | import org.springframework.util.StringUtils; |
36 | +import org.thingsboard.common.util.JacksonUtil; | ||
35 | import org.thingsboard.server.common.data.DeviceProfile; | 37 | import org.thingsboard.server.common.data.DeviceProfile; |
36 | import org.thingsboard.server.common.msg.EncryptionUtil; | 38 | import org.thingsboard.server.common.msg.EncryptionUtil; |
37 | import org.thingsboard.server.common.transport.TransportService; | 39 | import org.thingsboard.server.common.transport.TransportService; |
@@ -40,6 +42,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes | @@ -40,6 +42,7 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes | ||
40 | import org.thingsboard.server.common.transport.util.SslUtil; | 42 | import org.thingsboard.server.common.transport.util.SslUtil; |
41 | import org.thingsboard.server.gen.transport.TransportProtos; | 43 | import org.thingsboard.server.gen.transport.TransportProtos; |
42 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | 44 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
45 | +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; | ||
43 | 46 | ||
44 | import javax.annotation.PostConstruct; | 47 | import javax.annotation.PostConstruct; |
45 | import javax.security.auth.x500.X500Principal; | 48 | import javax.security.auth.x500.X500Principal; |
@@ -60,7 +63,7 @@ import java.util.concurrent.TimeUnit; | @@ -60,7 +63,7 @@ import java.util.concurrent.TimeUnit; | ||
60 | public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier { | 63 | public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier { |
61 | 64 | ||
62 | private final TransportService transportService; | 65 | private final TransportService transportService; |
63 | - private final TbLwM2MDtlsSessionStorage sessionStorage; | 66 | + private final TbLwM2MDtlsSessionStore sessionStorage; |
64 | private final LwM2MTransportServerConfig config; | 67 | private final LwM2MTransportServerConfig config; |
65 | 68 | ||
66 | @SuppressWarnings("deprecation") | 69 | @SuppressWarnings("deprecation") |
@@ -130,16 +133,24 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer | @@ -130,16 +133,24 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer | ||
130 | latch.countDown(); | 133 | latch.countDown(); |
131 | } | 134 | } |
132 | }); | 135 | }); |
133 | - latch.await(10, TimeUnit.SECONDS); | ||
134 | - ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0]; | ||
135 | - if (msg != null && strCert.equals(msg.getCredentials())) { | ||
136 | - credentialsBody = msg.getCredentials(); | ||
137 | - DeviceProfile deviceProfile = msg.getDeviceProfile(); | ||
138 | - if (msg.hasDeviceInfo() && deviceProfile != null) { | ||
139 | - String endpoint = sha3Hash; //TODO: extract endpoint from credentials body and push to storage | ||
140 | - sessionStorage.put(endpoint, msg); | 136 | + if (latch.await(10, TimeUnit.SECONDS)) { |
137 | + ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0]; | ||
138 | + if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) { | ||
139 | + JsonNode credentialsJson = JacksonUtil.toJsonNode(msg.getCredentials()); | ||
140 | + String certBody = credentialsJson.get("cert").asText(); | ||
141 | + String endpoint = credentialsJson.get("endpoint").asText(); | ||
142 | + if (strCert.equals(certBody)) { | ||
143 | + //TODO: extract endpoint from credentials body and push to storage | ||
144 | + credentialsBody = msg.getCredentials(); | ||
145 | + DeviceProfile deviceProfile = msg.getDeviceProfile(); | ||
146 | + if (msg.hasDeviceInfo() && deviceProfile != null) { | ||
147 | + sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg)); | ||
148 | + break; | ||
149 | + } | ||
150 | + } else { | ||
151 | + log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody); | ||
152 | + } | ||
141 | } | 153 | } |
142 | - break; | ||
143 | } | 154 | } |
144 | } catch (InterruptedException | | 155 | } catch (InterruptedException | |
145 | CertificateEncodingException | | 156 | CertificateEncodingException | |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java
renamed from
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsSessionStorage.java
@@ -15,12 +15,13 @@ | @@ -15,12 +15,13 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.lwm2m.secure; | 16 | package org.thingsboard.server.transport.lwm2m.secure; |
17 | 17 | ||
18 | +import lombok.Data; | ||
18 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | 19 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
19 | 20 | ||
20 | -public interface TbLwM2MDtlsSessionStorage { | 21 | +@Data |
22 | +public class TbX509DtlsSessionInfo { | ||
21 | 23 | ||
22 | - void put(String endpoint, ValidateDeviceCredentialsResponse msg); | ||
23 | - | ||
24 | - ValidateDeviceCredentialsResponse get(String endpoint); | 24 | + private final String x509CommonName; |
25 | + private final ValidateDeviceCredentialsResponse credentials; | ||
25 | 26 | ||
26 | } | 27 | } |
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; | @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; | ||
17 | 17 | ||
18 | import org.eclipse.leshan.server.registration.Registration; | 18 | import org.eclipse.leshan.server.registration.Registration; |
19 | import org.thingsboard.server.common.data.DeviceProfile; | 19 | import org.thingsboard.server.common.data.DeviceProfile; |
20 | +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | ||
20 | import org.thingsboard.server.gen.transport.TransportProtos; | 21 | import org.thingsboard.server.gen.transport.TransportProtos; |
21 | 22 | ||
22 | import java.util.Collection; | 23 | import java.util.Collection; |
@@ -59,4 +60,6 @@ public interface LwM2mClientContext { | @@ -59,4 +60,6 @@ public interface LwM2mClientContext { | ||
59 | Set<String> getSupportedIdVerInClient(Registration registration); | 60 | Set<String> getSupportedIdVerInClient(Registration registration); |
60 | 61 | ||
61 | LwM2mClient getClientByDeviceId(UUID deviceId); | 62 | LwM2mClient getClientByDeviceId(UUID deviceId); |
63 | + | ||
64 | + void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); | ||
62 | } | 65 | } |
@@ -21,6 +21,7 @@ import org.eclipse.leshan.server.registration.Registration; | @@ -21,6 +21,7 @@ import org.eclipse.leshan.server.registration.Registration; | ||
21 | import org.eclipse.leshan.server.security.EditableSecurityStore; | 21 | import org.eclipse.leshan.server.security.EditableSecurityStore; |
22 | import org.springframework.stereotype.Service; | 22 | import org.springframework.stereotype.Service; |
23 | import org.thingsboard.server.common.data.DeviceProfile; | 23 | import org.thingsboard.server.common.data.DeviceProfile; |
24 | +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | ||
24 | import org.thingsboard.server.gen.transport.TransportProtos; | 25 | import org.thingsboard.server.gen.transport.TransportProtos; |
25 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | 26 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
26 | import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo; | 27 | import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo; |
@@ -137,6 +138,13 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { | @@ -137,6 +138,13 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { | ||
137 | } | 138 | } |
138 | 139 | ||
139 | @Override | 140 | @Override |
141 | + public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) { | ||
142 | + LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID()); | ||
143 | + lwM2mClientsByEndpoint.put(registration.getEndpoint(), client); | ||
144 | + lwM2mClientsByRegistrationId.put(registration.getId(), client); | ||
145 | + } | ||
146 | + | ||
147 | + @Override | ||
140 | public Collection<LwM2mClient> getLwM2mClients() { | 148 | public Collection<LwM2mClient> getLwM2mClients() { |
141 | return lwM2mClientsByEndpoint.values(); | 149 | return lwM2mClientsByEndpoint.values(); |
142 | } | 150 | } |
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.springframework.stereotype.Component; | ||
19 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | ||
20 | +import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo; | ||
21 | + | ||
22 | +import java.util.concurrent.ConcurrentHashMap; | ||
23 | + | ||
24 | +@Component | ||
25 | +@TbLwM2mTransportComponent | ||
26 | +public class TbL2M2MDtlsSessionInMemoryStore implements TbLwM2MDtlsSessionStore { | ||
27 | + | ||
28 | + private final ConcurrentHashMap<String, TbX509DtlsSessionInfo> store = new ConcurrentHashMap<>(); | ||
29 | + | ||
30 | + @Override | ||
31 | + public void put(String endpoint, TbX509DtlsSessionInfo msg) { | ||
32 | + store.put(endpoint, msg); | ||
33 | + } | ||
34 | + | ||
35 | + @Override | ||
36 | + public TbX509DtlsSessionInfo get(String endpoint) { | ||
37 | + return store.get(endpoint); | ||
38 | + } | ||
39 | +} |
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 | + | ||
19 | +import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo; | ||
20 | + | ||
21 | +public interface TbLwM2MDtlsSessionStore { | ||
22 | + | ||
23 | + void put(String endpoint, TbX509DtlsSessionInfo msg); | ||
24 | + | ||
25 | + TbX509DtlsSessionInfo get(String endpoint); | ||
26 | + | ||
27 | + //TODO: add way to delete the session by endpoint. | ||
28 | + | ||
29 | +} |
@@ -20,12 +20,16 @@ import org.eclipse.leshan.server.security.EditableSecurityStore; | @@ -20,12 +20,16 @@ import org.eclipse.leshan.server.security.EditableSecurityStore; | ||
20 | import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; | 20 | import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; |
21 | import org.eclipse.leshan.server.security.SecurityInfo; | 21 | import org.eclipse.leshan.server.security.SecurityInfo; |
22 | import org.eclipse.leshan.server.security.SecurityStoreListener; | 22 | import org.eclipse.leshan.server.security.SecurityStoreListener; |
23 | +import org.springframework.stereotype.Component; | ||
24 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | ||
23 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | 25 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
24 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | 26 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; |
25 | 27 | ||
26 | import java.util.Collection; | 28 | import java.util.Collection; |
27 | 29 | ||
28 | @Slf4j | 30 | @Slf4j |
31 | +@Component | ||
32 | +@TbLwM2mTransportComponent | ||
29 | public class TbLwM2mSecurityStore implements EditableSecurityStore { | 33 | public class TbLwM2mSecurityStore implements EditableSecurityStore { |
30 | 34 | ||
31 | private final LwM2mClientContext clientContext; | 35 | private final LwM2mClientContext clientContext; |