Skip to content

Commit

Permalink
Merge pull request #342 from OktopUSP/dev
Browse files Browse the repository at this point in the history
USP/CWMP Messages Templates
  • Loading branch information
leandrofars authored Oct 30, 2024
2 parents 11e86f2 + 509b90c commit e4704d8
Show file tree
Hide file tree
Showing 25 changed files with 1,414 additions and 653 deletions.
2 changes: 2 additions & 0 deletions backend/services/acs/internal/server/handler/cwmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ func (h *Handler) CwmpHandler(w http.ResponseWriter, r *http.Request) {
h.pub(NATS_CWMP_SUBJECT_PREFIX+sn+".info", tmp)
}

cpe.ConnectionRequestURL = Inform.GetConnectionRequest() // Update connection request URL, in case the CPE changed IP

log.Printf("Received an Inform from device %s withEventCodes %s", addr, Inform.GetEvents())

expiration := time.Now().AddDate(0, 0, 1)
Expand Down
6 changes: 6 additions & 0 deletions backend/services/controller/internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func (a *Api) StartApi() {
iot := r.PathPrefix("/api/device").Subrouter()
iot.HandleFunc("/alias", a.setDeviceAlias).Methods("PUT")
iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "POST", "DELETE")
iot.HandleFunc("/message/{type}", a.addTemplate).Methods("POST")
iot.HandleFunc("/message", a.updateTemplate).Methods("PUT")
iot.HandleFunc("/message", a.getTemplate).Methods("GET")
iot.HandleFunc("/message", a.deleteTemplate).Methods("DELETE")
iot.HandleFunc("/cwmp/{sn}/generic", a.cwmpGenericMsg).Methods("PUT")
iot.HandleFunc("/cwmp/{sn}/getParameterNames", a.cwmpGetParameterNamesMsg).Methods("PUT")
iot.HandleFunc("/cwmp/{sn}/getParameterValues", a.cwmpGetParameterValuesMsg).Methods("PUT")
iot.HandleFunc("/cwmp/{sn}/getParameterAttributes", a.cwmpGetParameterAttributesMsg).Methods("PUT")
Expand All @@ -61,6 +66,7 @@ func (a *Api) StartApi() {
iot.HandleFunc("/cwmp/{sn}/deleteObject", a.cwmpDeleteObjectMsg).Methods("PUT")
iot.HandleFunc("", a.retrieveDevices).Methods("GET")
iot.HandleFunc("/filterOptions", a.filterOptions).Methods("GET")
iot.HandleFunc("/{sn}/{mtp}/generic", a.deviceGenericMessage).Methods("PUT")
iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT")
iot.HandleFunc("/{sn}/{mtp}/add", a.deviceCreateMsg).Methods("PUT")
iot.HandleFunc("/{sn}/{mtp}/del", a.deviceDeleteMsg).Methods("PUT")
Expand Down
27 changes: 26 additions & 1 deletion backend/services/controller/internal/api/cwmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,31 @@ import (

var errDeviceModelNotFound = errors.New("device model not found")

func (a *Api) cwmpGenericMsg(w http.ResponseWriter, r *http.Request) {

sn := getSerialNumberFromRequest(r)

payload, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall(err.Error()))
return
}

if len(payload) == 0 {
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall("Empty payload"))
return
}

data, _, err := cwmpInteraction[cwmp.SoapEnvelope](sn, payload, w, a.nc)
if err != nil {
return
}

w.Write(data)
}

func (a *Api) cwmpGetParameterNamesMsg(w http.ResponseWriter, r *http.Request) {
sn := getSerialNumberFromRequest(r)

Expand Down Expand Up @@ -125,7 +150,7 @@ func (a *Api) cwmpDeleteObjectMsg(w http.ResponseWriter, r *http.Request) {
w.Write(data)
}

func cwmpInteraction[T cwmp.SetParameterValuesResponse | cwmp.DeleteObjectResponse | cwmp.GetParameterAttributesResponse | cwmp.GetParameterNamesResponse | cwmp.GetParameterValuesResponse | cwmp.AddObjectResponse](
func cwmpInteraction[T cwmp.SetParameterValuesResponse | cwmp.SoapEnvelope | cwmp.DeleteObjectResponse | cwmp.GetParameterAttributesResponse | cwmp.GetParameterNamesResponse | cwmp.GetParameterValuesResponse | cwmp.AddObjectResponse](
sn string, payload []byte, w http.ResponseWriter, nc *nats.Conn,
) ([]byte, T, error) {

Expand Down
139 changes: 139 additions & 0 deletions backend/services/controller/internal/api/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"strings"

"github.com/gorilla/mux"
"github.com/leandrofars/oktopus/internal/bridge"
"github.com/leandrofars/oktopus/internal/db"
"github.com/leandrofars/oktopus/internal/entity"
Expand Down Expand Up @@ -367,3 +368,141 @@ func (a *Api) filterOptions(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(resp.Code)
w.Write(utils.Marshall(resp.Msg))
}

func (a *Api) updateTemplate(w http.ResponseWriter, r *http.Request) {

name := r.URL.Query().Get("name")
if name == "" {
w.WriteHeader(http.StatusBadRequest)
utils.MarshallEncoder("No name provided", w)
return
}

payload, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
utils.MarshallEncoder("Error to decode payload: "+err.Error(), w)
return
}

payloadLen := len(payload)
if payloadLen == 0 {
w.WriteHeader(http.StatusBadRequest)
utils.MarshallEncoder("No payload provided", w)
return
}

err = a.db.UpdateTemplate(name, string(payload))
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(err.Error())
return
}

w.WriteHeader(http.StatusNoContent)
}

func (a *Api) addTemplate(w http.ResponseWriter, r *http.Request) {

name := r.URL.Query().Get("name")
if name == "" {
w.WriteHeader(http.StatusBadRequest)
utils.MarshallEncoder("No name provided", w)
return
}

payload, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
utils.MarshallEncoder("Error to decode payload: "+err.Error(), w)
return
}

payloadLen := len(payload)
if payloadLen == 0 {
w.WriteHeader(http.StatusBadRequest)
utils.MarshallEncoder("No payload provided", w)
return
}

vars := mux.Vars(r)
switch vars["type"] {
case "cwmp":
err = a.db.AddTemplate(name, "cwmp", string(payload))
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(err.Error())
return
}
w.WriteHeader(http.StatusNoContent)
return
case "usp":
err = a.db.AddTemplate(name, "usp", string(payload))
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(err.Error())
return
}
w.WriteHeader(http.StatusNoContent)
return
default:
w.WriteHeader(http.StatusBadRequest)
utils.MarshallEncoder("Invalid template type", w)
return
}
}

func (a *Api) getTemplate(w http.ResponseWriter, r *http.Request) {

name := r.URL.Query().Get("name")
msgType := r.URL.Query().Get("type")

if name == "" {

var filter bson.D
if msgType == "" {
filter = bson.D{}
} else {
filter = bson.D{{"type", msgType}}
}

result, err := a.db.AllTemplates(filter)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode("Error to get all templates: " + err.Error())
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(result)
return
} else {
t, err := a.db.FindTemplate(bson.D{{"name", name}})
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode("error to find message: " + err.Error())
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(t.Value))
return
}

}

func (a *Api) deleteTemplate(w http.ResponseWriter, r *http.Request) {

name := r.URL.Query().Get("name")
if name == "" {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode("needs template name!")
return
} else {
err := a.db.DeleteTemplate(name)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode("error to delete template: " + err.Error())
return
}
w.WriteHeader(http.StatusNoContent)
}
}
17 changes: 7 additions & 10 deletions backend/services/controller/internal/api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,6 @@ func (a *Api) changePassword(w http.ResponseWriter, r *http.Request) {
return
}

userToChangePasswd := mux.Vars(r)["user"]
if userToChangePasswd != "" && userToChangePasswd != email {
rUser, _ := a.db.FindUser(email)
if rUser.Level != db.AdminUser {
w.WriteHeader(http.StatusForbidden)
return
}
email = userToChangePasswd
}

var user db.User
err = json.NewDecoder(r.Body).Decode(&user)
if err != nil {
Expand All @@ -154,6 +144,12 @@ func (a *Api) changePassword(w http.ResponseWriter, r *http.Request) {
}
user.Email = email

if len(user.Password) < 8 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Password must be at least 8 characters long"))
return
}

if err := user.HashPassword(user.Password); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
Expand All @@ -164,6 +160,7 @@ func (a *Api) changePassword(w http.ResponseWriter, r *http.Request) {
return
}

w.WriteHeader(http.StatusNoContent)
}

func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) {
Expand Down
50 changes: 50 additions & 0 deletions backend/services/controller/internal/api/usp.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"io"
"log"
"net/http"

Expand All @@ -12,6 +13,7 @@ import (
"github.com/leandrofars/oktopus/internal/usp/usp_utils"
"github.com/leandrofars/oktopus/internal/utils"
"github.com/nats-io/nats.go"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -59,6 +61,16 @@ func sendUspMsg(msg usp_msg.Msg, sn string, w http.ResponseWriter, nc *nats.Conn
}

body := receivedMsg.Body.GetResponse()
if body == nil {
errorMsg := receivedMsg.Body.GetError()
if errorMsg == nil {
w.WriteHeader(http.StatusInternalServerError)
log.Println("No response body or error")
return nil
}
w.Write(utils.Marshall(errorMsg))
return nil
}

switch body.RespType.(type) {
case *usp_msg.Response_GetResp:
Expand Down Expand Up @@ -86,6 +98,44 @@ func sendUspMsg(msg usp_msg.Msg, sn string, w http.ResponseWriter, nc *nats.Conn
return nil
}

func (a *Api) deviceGenericMessage(w http.ResponseWriter, r *http.Request) {

sn := getSerialNumberFromRequest(r)
mtp, err := getMtpFromRequest(r, w)
if err != nil {
return
}

if mtp == "" {
var ok bool
mtp, ok = deviceStateOK(w, a.nc, sn)
if !ok {
return
}
}

payload, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall(err.Error()))
return
}

var msg usp_msg.Msg

err = protojson.Unmarshal(payload, &msg)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write(utils.Marshall(err.Error()))
return
}

err = sendUspMsg(msg, sn, w, a.nc, mtp)
if err != nil {
return
}
}

func (a *Api) deviceGetMsg(w http.ResponseWriter, r *http.Request) {

sn := getSerialNumberFromRequest(r)
Expand Down
17 changes: 14 additions & 3 deletions backend/services/controller/internal/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
)

type Database struct {
client *mongo.Client
users *mongo.Collection
ctx context.Context
client *mongo.Client
users *mongo.Collection
template *mongo.Collection
ctx context.Context
}

func NewDatabase(ctx context.Context, mongoUri string) Database {
Expand Down Expand Up @@ -43,6 +44,16 @@ func NewDatabase(ctx context.Context, mongoUri string) Database {
log.Fatalln(err)
}

db.template = client.Database("general").Collection("templates")
indexField = bson.M{"name": 1}
_, err = db.template.Indexes().CreateOne(ctx, mongo.IndexModel{
Keys: indexField,
Options: options.Index().SetUnique(true),
})
if err != nil {
log.Fatalln(err)
}

db.ctx = ctx

return db
Expand Down
Loading

0 comments on commit e4704d8

Please sign in to comment.