lorenzodonini / ocpp-go

Open Charge Point Protocol implementation in Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fetch chargePointId inside SetBasichandler

Mayuri-Mense opened this issue · comments

How do we fetch connectionId/chargePointId inside SetBasicHandler to have below checks

  1. ChargePointId specified in URL matches with username
  2. 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:

  1. check the basic auth, save the result in a custom struct in the callback
  2. wait for the websocket upgrade to finish
  3. 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