11package de .rwth .idsg .steve .ocpp .soap ;
22
33import de .rwth .idsg .steve .ocpp .OcppProtocol ;
4+ import de .rwth .idsg .steve .repository .ChargePointRepository ;
45import de .rwth .idsg .steve .repository .OcppServerRepository ;
56import de .rwth .idsg .steve .repository .impl .ChargePointRepositoryImpl ;
67import lombok .extern .slf4j .Slf4j ;
8+ import org .apache .cxf .binding .soap .Soap12 ;
9+ import org .apache .cxf .binding .soap .SoapFault ;
710import org .apache .cxf .interceptor .Fault ;
811import org .apache .cxf .message .Message ;
912import org .apache .cxf .message .MessageContentsList ;
1720import org .springframework .beans .factory .annotation .Autowired ;
1821import org .springframework .stereotype .Component ;
1922
23+ import javax .xml .namespace .QName ;
2024import java .util .concurrent .ScheduledExecutorService ;
2125
2226import static org .apache .cxf .ws .addressing .JAXWSAConstants .ADDRESSING_PROPERTIES_INBOUND ;
2327
2428/**
25- * Intercepts incoming OCPP messages to update the endpoint address ("From" field of the WS-A header) in DB.
29+ * 1. Checks the registration status of a station for operations other than BootNotification.
30+ *
31+ * 2. Intercepts incoming OCPP messages to update the endpoint address ("From" field of the WS-A header) in DB.
2632 * And the absence of the field is not a deal breaker anymore. But, as a side effect, the user will not be able
2733 * to send commands to the charging station, since the DB call to list the charge points will filter it out. See
2834 * {@link ChargePointRepositoryImpl#getChargePointSelect(OcppProtocol)}.
3541public class FromAddressInterceptor extends AbstractPhaseInterceptor <Message > {
3642
3743 @ Autowired private OcppServerRepository ocppServerRepository ;
44+ @ Autowired private ChargePointRepository chargePointRepository ;
3845 @ Autowired private ScheduledExecutorService executorService ;
3946
47+ private static final String BOOT_OPERATION_NAME = "BootNotification" ;
4048 private static final String CHARGEBOX_ID_HEADER = "ChargeBoxIdentity" ;
4149
4250 public FromAddressInterceptor () {
@@ -45,36 +53,48 @@ public FromAddressInterceptor() {
4553
4654 @ Override
4755 public void handleMessage (Message message ) throws Fault {
48- executorService .execute (() -> handleMessageInternal (message ));
49- }
56+ String chargeBoxId = getChargeBoxId (message );
5057
51- private void handleMessageInternal (Message message ) {
52- try {
53- String chargeBoxId = getChargeBoxId (message );
54- String endpointAddress = getEndpointAddress (message );
58+ // -------------------------------------------------------------------------
59+ // 1. check registration for operations other than BootNotification
60+ // -------------------------------------------------------------------------
5561
56- if (chargeBoxId != null && endpointAddress != null ) {
57- ocppServerRepository .updateEndpointAddress (chargeBoxId , endpointAddress );
62+ QName opName = message .getExchange ().getBindingOperationInfo ().getOperationInfo ().getName ();
63+
64+ if (!BOOT_OPERATION_NAME .equals (opName .getLocalPart ())) {
65+ if (!chargePointRepository .isRegistered (chargeBoxId )) {
66+ throw createAuthFault (opName );
5867 }
59- } catch (Exception e ) {
60- log .error ("Exception occurred" , e );
6168 }
69+
70+ // -------------------------------------------------------------------------
71+ // 2. update endpoint
72+ // -------------------------------------------------------------------------
73+
74+ executorService .execute (() -> {
75+ try {
76+ String endpointAddress = getEndpointAddress (message );
77+ if (endpointAddress != null ) {
78+ ocppServerRepository .updateEndpointAddress (chargeBoxId , endpointAddress );
79+ }
80+ } catch (Exception e ) {
81+ log .error ("Exception occurred" , e );
82+ }
83+ });
6284 }
6385
6486 private String getChargeBoxId (Message message ) {
6587 MessageContentsList lst = MessageContentsList .getContentsList (message );
66- if (lst == null ) {
67- return null ;
68- }
69-
70- MessageInfo mi = (MessageInfo ) message .get ("org.apache.cxf.service.model.MessageInfo" );
71- for (MessagePartInfo mpi : mi .getMessageParts ()) {
72- if (CHARGEBOX_ID_HEADER .equals (mpi .getName ().getLocalPart ())) {
73- return (String ) lst .get (mpi );
88+ if (lst != null ) {
89+ MessageInfo mi = (MessageInfo ) message .get ("org.apache.cxf.service.model.MessageInfo" );
90+ for (MessagePartInfo mpi : mi .getMessageParts ()) {
91+ if (CHARGEBOX_ID_HEADER .equals (mpi .getName ().getLocalPart ())) {
92+ return (String ) lst .get (mpi );
93+ }
7494 }
7595 }
76-
77- return null ;
96+ // should not happen
97+ throw createSpecFault ( message . getExchange (). getBindingOperationInfo (). getOperationInfo (). getName ()) ;
7898 }
7999
80100 private String getEndpointAddress (Message message ) {
@@ -90,4 +110,20 @@ private String getEndpointAddress(Message message) {
90110 return from .getAddress ().getValue ();
91111 }
92112 }
113+
114+ private static SoapFault createAuthFault (QName qName ) {
115+ // as defined by OCPP spec
116+ String message = "Sender failed authentication or is not authorized to use the requested operation." ;
117+ SoapFault sf = new SoapFault (message , Soap12 .getInstance ().getSender ());
118+ sf .addSubCode (new QName (qName .getNamespaceURI (), "SecurityError" ));
119+ return sf ;
120+ }
121+
122+ private static SoapFault createSpecFault (QName qName ) {
123+ // as defined by OCPP spec
124+ String message = "Sender's message does not comply with protocol specification." ;
125+ SoapFault sf = new SoapFault (message , Soap12 .getInstance ().getSender ());
126+ sf .addSubCode (new QName (qName .getNamespaceURI (), "ProtocolError" ));
127+ return sf ;
128+ }
93129}
0 commit comments