Fetch chargePointId inside SetBasichandler
Mayuri-Mense opened this issue · 6 comments
How do we fetch connectionId/chargePointId inside SetBasicHandler to have below checks
- ChargePointId specified in URL matches with username
- Fetch credentials from db for specified charge point
func setupCentralSystem() ocpp16.CentralSystem {
server := ws.NewServer()
//Authorize charging Point
server.SetBasicAuthHandler(func(username string, password string) bool {
validCredentials := "username" == username && "password" == password
return validCredentials
})
return ocpp16.NewCentralSystem(nil, server)
}
func main(){
..
centralSystem.Start(listenPort, "/ocpp/{ws}")
..
}
Please suggest
Basic auth is a pure user+pass authentication scheme for HTTP and is unrelated to any additional application protocol running on top. If the username is identical to the chargePointID by design, then you can simply query the DB during the basic auth procedure.
Here's a few options for making sure the identifier matches the username.
CheckClientHandler
server.SetCheckClientHandler(func(id string, r *http.Request) bool {
// username, password := r.BasicAuth()
// <Your logic>
return true
})
However be advised that returning false
in that function will simply lead to a 403
error without setting any special headers (such as WWW-Authenticate
)
Custom flow
Setup a custom login flow:
- check the basic auth, save the result in a custom struct in the callback
- wait for the websocket upgrade to finish
- wait for the
newClientHandler
to pass the chargePointID and compare against the temporary result- technically this is prone to well-timed spoofing attacks, hence would only recommend in a closed scenario
Contribute
I happily accept contributions. For example you may change the SetBasicAuthHandler
callback to also accept additional params (such as the raw HTTP request to retrieve the ID). The handler is invoked here. This will be a breaking change for existing implementations though.
To clarify more, I am trying to use SetBasicAuthHandler with the websocket connection, so when charger connects to websocket server, it specifies chargepointid in the URL, also the same chargePointId is specified in Basic username:password [here username: chargePointId].
URL: ws://localhost:80/ocpp/cp1
Headers: Authorization Basic cp1:password
Need to match chargepointId specified in URL with username in Basic
SetCheckClientHandler doesn't seems to provide that functionality
SetCheckClientHandler doesn't seems to provide that functionality
Why not? The handler provides the id
(which is the chargePointId) as well as the raw HTTP request before the websocket upgrade, which contains the basic auth credentials (if given). This was shown in the pseudo-code above. What information is missing to achieve your desired behavior?
Right thats what i had expected, but it doesnt seem to work.
Please @lorenzodonini can you correct me what wrong am i doing here ?
var centralSystem ocpp16.CentralSystem
func setupCentralSystem() ocpp16.CentralSystem {
server := ws.NewServer()
//todo remove
server.SetCheckOriginHandler(func(r *http.Request) bool {
fmt.Println("Here origin")
return true
})
server.SetCheckClientHandler(func(id string, r *http.Request) bool {
fmt.Println("Here in set check client handler")
return true
})
//Authorize charging Point
server.SetBasicAuthHandler(func(username string, password string) bool {
//fetch details from db
res,err := utils.GetOcppNodeById(username)
if err != nil {
return false
}
//fetch auth key by chargePointId
validCredentials := res.ChargePointId == username && res.Key == password
return validCredentials
})
return ocpp16.NewCentralSystem(nil, server)
}
// Start function
func main() {
// Load config from ENV
var listenPort = defaultListenPort
port := espenv.GetOcppGatewayPort()
if p, err := strconv.Atoi(port); err == nil {
listenPort = p
} else {
log.Printf("no valid %v environment variable found, using default port", espenv.GetOcppGatewayPort())
}
// Prepare OCPP 1.6 central system
centralSystem = setupCentralSystem()
// Support callbacks for all OCPP 1.6 profiles
handler := &rmcore.CentralSystemHandler{ChargePoints: map[string]*rmcore.ChargePointState{}}
centralSystem.SetCoreHandler(handler)
centralSystem.SetLocalAuthListHandler(handler)
centralSystem.SetFirmwareManagementHandler(handler)
// Add handlers for dis/connection of charge points
centralSystem.SetNewChargePointHandler(func(chargePoint ocpp16.ChargePointConnection) {
handler.ChargePoints[chargePoint.ID()] = &rmcore.ChargePointState{Connectors: map[int]*rmcore.ConnectorInfo{}, Transactions: map[int]*rmcore.TransactionInfo{}}
rmcore.Log.WithField("client", chargePoint.ID()).Info("new charge point connected")
})
centralSystem.SetChargePointDisconnectedHandler(func(chargePoint ocpp16.ChargePointConnection) {
rmcore.Log.WithField("client", chargePoint.ID()).Info("charge point disconnected")
delete(handler.ChargePoints, chargePoint.ID())
})
ocppj.SetLogger(rmcore.Log.WithField("logger", "ocppj"))
ws.SetLogger(rmcore.Log.WithField("logger", "websocket"))
// Run central system
rmcore.Log.Infof("starting central system on port %v", listenPort)
centralSystem.Start(listenPort, "/ocpp/{ws}")
rmcore.Log.Info("stopped central system")
}
the fmt.Println("Here in set check client handler"), doesnt gets invoked when i try to connect charging point to server.
SetBasicAuthHandler, SetCheckOriginHandler seem to work but NOT SetCheckClientHandler.
The handler gets overwritten when the server is started in the current implementation. This is a bit cumbersome and I'll try to fix it when I have some time.
The "workaround" is to set the handler from the top level:
centralSystem.SetNewChargingStationValidationHandler(func(id string, r *http.Request) bool {
return true
})
Thank you so much