From 34531c2d3bad2dd21645d93c251a21f3e6d409c5 Mon Sep 17 00:00:00 2001 From: Simon Bernard Date: Thu, 10 Oct 2024 16:36:12 +0200 Subject: [PATCH] Use Authorizer in SendHandler --- .../eclipse/leshan/server/LeshanServer.java | 7 +- .../server/security/DefaultAuthorizer.java | 45 +++++- .../leshan/server/send/SendHandler.java | 130 ++++++++++++------ 3 files changed, 129 insertions(+), 53 deletions(-) diff --git a/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/LeshanServer.java b/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/LeshanServer.java index d9a31460f4..2654e27ef6 100644 --- a/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/LeshanServer.java +++ b/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/LeshanServer.java @@ -163,7 +163,7 @@ public LeshanServer(LwM2mServerEndpointsProvider endpointsProvider, Registration presenceService = createPresenceService(registrationService, awakeTimeProvider, updateRegistrationOnNotification); } - this.sendService = createSendHandler(registrationStore, updateRegistrationOnSend); + this.sendService = createSendHandler(registrationStore, authorizer, updateRegistrationOnSend); // create endpoints ServerEndpointToolbox toolbox = new ServerEndpointToolbox(decoder, encoder, linkParser, @@ -202,8 +202,9 @@ protected PresenceServiceImpl createPresenceService(RegistrationService registra return presenceService; } - protected SendHandler createSendHandler(RegistrationStore registrationStore, boolean updateRegistrationOnSend) { - return new SendHandler(registrationStore, updateRegistrationOnSend); + protected SendHandler createSendHandler(RegistrationStore registrationStore, Authorizer authorizer, + boolean updateRegistrationOnSend) { + return new SendHandler(registrationStore, authorizer, updateRegistrationOnSend); } protected UplinkDeviceManagementRequestReceiver createRequestReceiver(RegistrationHandler registrationHandler, diff --git a/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/security/DefaultAuthorizer.java b/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/security/DefaultAuthorizer.java index 6d434cacb8..898da06c80 100644 --- a/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/security/DefaultAuthorizer.java +++ b/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/security/DefaultAuthorizer.java @@ -17,7 +17,10 @@ import org.eclipse.leshan.core.endpoint.EndpointUri; import org.eclipse.leshan.core.peer.LwM2mPeer; +import org.eclipse.leshan.core.request.DeregisterRequest; import org.eclipse.leshan.core.request.RegisterRequest; +import org.eclipse.leshan.core.request.SendRequest; +import org.eclipse.leshan.core.request.UpdateRequest; import org.eclipse.leshan.core.request.UplinkRequest; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.servers.security.Authorization; @@ -72,15 +75,43 @@ protected boolean checkEndpointUri(UplinkRequest request, Registration regist protected Authorization checkIdentity(UplinkRequest request, Registration registration, LwM2mPeer sender, EndpointUri endpointUri) { - // do we have security information for this client? - SecurityInfo expectedSecurityInfo = null; - if (securityStore != null) - expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint()); - if (securityChecker.checkSecurityInfo(registration.getEndpoint(), sender, expectedSecurityInfo)) { - return Authorization.approved(); + if (request instanceof RegisterRequest // + || request instanceof UpdateRequest || request instanceof DeregisterRequest) { + + // TODO we should think what should be the right behavior here and maybe changed it if needed + // Meanwhile, to not change behavior we do not check security on with : + // - updateRegistrationOnSend mode + // - updateRegistrationOnNotification mode + // Those modes are out of specification and not recommended. + if ( + // We use HACK to know if those mode are used + + // means updateRegistrationOnSend is used (because trigger by given SEND request) + request.getCoapRequest() instanceof SendRequest) // + { + return Authorization.approved(); + } + + // For Register, Update, DeregisterRequest we check in security store + // do we have security information for this client? + SecurityInfo expectedSecurityInfo = null; + if (securityStore != null) + expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint()); + + if (securityChecker.checkSecurityInfo(registration.getEndpoint(), sender, expectedSecurityInfo)) { + return Authorization.approved(); + } else { + return Authorization.declined(); + } } else { - return Authorization.declined(); + // for other we just check this is same identity + if (registration.getClientTransportData().getIdentity().equals(sender.getIdentity())) { + return Authorization.approved(); + } else { + return Authorization.declined(); + } } } + } diff --git a/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/send/SendHandler.java b/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/send/SendHandler.java index 278308352c..c2e3b07e79 100644 --- a/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/send/SendHandler.java +++ b/leshan-lwm2m-server/src/main/java/org/eclipse/leshan/server/send/SendHandler.java @@ -19,12 +19,14 @@ import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; +import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.endpoint.EndpointUri; import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.TimestampedLwM2mNodes; import org.eclipse.leshan.core.peer.LwM2mPeer; import org.eclipse.leshan.core.request.SendRequest; +import org.eclipse.leshan.core.request.UpdateRequest; import org.eclipse.leshan.core.request.exception.InvalidRequestException; import org.eclipse.leshan.core.response.SendResponse; import org.eclipse.leshan.core.response.SendableResponse; @@ -32,6 +34,8 @@ import org.eclipse.leshan.server.registration.RegistrationStore; import org.eclipse.leshan.server.registration.RegistrationUpdate; import org.eclipse.leshan.server.registration.UpdatedRegistration; +import org.eclipse.leshan.server.security.Authorizer; +import org.eclipse.leshan.servers.security.Authorization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,12 +49,14 @@ public class SendHandler implements SendService { private final Logger LOG = LoggerFactory.getLogger(SendHandler.class); private final RegistrationStore registrationStore; + private final Authorizer authorizer; private final boolean updateRegistrationOnSend; private final List listeners = new CopyOnWriteArrayList<>();; - public SendHandler(RegistrationStore registrationStore, boolean updateRegistrationOnSend) { + public SendHandler(RegistrationStore registrationStore, Authorizer authorizer, boolean updateRegistrationOnSend) { this.registrationStore = registrationStore; + this.authorizer = authorizer; this.updateRegistrationOnSend = updateRegistrationOnSend; } @@ -67,60 +73,85 @@ public void removeListener(SendListener listener) { public SendableResponse handleSend(LwM2mPeer sender, Registration registration, final SendRequest request, EndpointUri serverEndpointUri) { - // try to update registration if needed + // Try to update registration if needed final Registration updatedRegistration; try { - updatedRegistration = updateRegistration(sender, registration); + updatedRegistration = updateRegistration(sender, registration, request, serverEndpointUri); + if (updatedRegistration == null) { + return errorReponse(updatedRegistration, ResponseCode.BAD_REQUEST, "not authorized", null); + } } catch (Exception e) { - String errMsg = "unable to update registration"; - SendableResponse response = new SendableResponse<>(SendResponse.internalServerError(errMsg), - new Runnable() { - @Override - public void run() { - onError(registration, errMsg, e); - } - }); - return response; + return errorReponse(registration, ResponseCode.INTERNAL_SERVER_ERROR, "unable to update registration", e); + } + + // Check if send request is allowed + Authorization authorized = authorizer.isAuthorized(request, registration, sender, serverEndpointUri); + if (authorized.isDeclined()) { + return errorReponse(updatedRegistration, ResponseCode.BAD_REQUEST, "not authorized", null); } - // Send Response to send request on success + // Validate and create Send Response final SendResponse sendResponse = validateSendRequest(updatedRegistration, request); - SendableResponse response = new SendableResponse<>(sendResponse, new Runnable() { - - @Override - public void run() { - if (sendResponse.isSuccess()) { - fireDataReceived(updatedRegistration, request.getTimestampedNodes(), request); - } else { - onError(updatedRegistration, String.format("Invalid Send Request, server returns %s %s", // - sendResponse.getCode().getName(), // - sendResponse.getErrorMessage() != null ? "because" + sendResponse.getErrorMessage() : ""), - new InvalidRequestException( - sendResponse.getErrorMessage() != null ? sendResponse.getErrorMessage() - : "unknown reason")); - } - } - }); + SendableResponse response; + if (sendResponse.isSuccess()) { + response = new SendableResponse<>(sendResponse, new Runnable() { + @Override + public void run() { + if (sendResponse.isSuccess()) { + fireDataReceived(updatedRegistration, request.getTimestampedNodes(), request); + } + } + }); + } else { + response = errorReponse(updatedRegistration, // + sendResponse.getCode(), // + String.format("Invalid Send Request, server returns %s %s", // + sendResponse.getCode().getName(), // + sendResponse.getErrorMessage() != null ? "because" + sendResponse.getErrorMessage() : ""), // + new InvalidRequestException(sendResponse.getErrorMessage() != null ? sendResponse.getErrorMessage() + : "unknown reason")); + } return response; } - private Registration updateRegistration(LwM2mPeer sender, final Registration registration) { - if (updateRegistrationOnSend) { - RegistrationUpdate regUpdate = new RegistrationUpdate(registration.getId(), sender, null, null, null, null, - null, null, null, null, null, null); - UpdatedRegistration updatedRegistration = registrationStore.updateRegistration(regUpdate); - if (updatedRegistration == null || updatedRegistration.getUpdatedRegistration() == null) { - String errorMsg = String.format( - "Unexpected error when receiving Send Request: There is no registration with id %s", - registration.getId()); - LOG.error(errorMsg); - throw new IllegalStateException(errorMsg); - } - return updatedRegistration.getUpdatedRegistration(); + /** + * Update registration if needed and allowed by authorizer. + * + * Note that updating registration on send is out of specification and could be a problem for interoperability. + * + * @return the registration or the updated registration or null if not allowed to update. + */ + protected Registration updateRegistration(LwM2mPeer sender, final Registration registration, + final SendRequest request, EndpointUri endpointUri) { + if (!updateRegistrationOnSend) { + // mode is not activate so we don't update registration + return registration; + + } + + // check if update is allowed + // HACK we create and Update request + // it can be identified because we pass the SEND request as under-layer object) + UpdateRequest updateRequest = new UpdateRequest(registration.getId(), null, null, null, null, null, request); + Authorization authorized = authorizer.isAuthorized(updateRequest, registration, sender, endpointUri); + if (authorized.isDeclined()) { + return null; + } + + // update registration + RegistrationUpdate regUpdate = new RegistrationUpdate(registration.getId(), sender, null, null, null, null, + null, null, null, null, null, null); + UpdatedRegistration updatedRegistration = registrationStore.updateRegistration(regUpdate); + if (updatedRegistration == null || updatedRegistration.getUpdatedRegistration() == null) { + String errorMsg = String.format( + "Unexpected error when receiving Send Request: There is no registration with id %s", + registration.getId()); + LOG.error(errorMsg); + throw new IllegalStateException(errorMsg); } - return registration; + return updatedRegistration.getUpdatedRegistration(); } protected void fireDataReceived(Registration registration, TimestampedLwM2mNodes data, SendRequest request) { @@ -156,4 +187,17 @@ protected SendResponse validateSendRequest(Registration registration, SendReques } return SendResponse.success(); } + + protected SendableResponse errorReponse(Registration registration, ResponseCode code, + String errorMessage, Exception e) { + + SendableResponse response = new SendableResponse<>(new SendResponse(code, errorMessage), + new Runnable() { + @Override + public void run() { + onError(registration, errorMessage, e); + } + }); + return response; + } }