diff --git a/.gitignore b/.gitignore index 8846f37d..ddc9524e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ log/ vendor/ public/ +node_modules/ +yarn.lock + # emacs/vim GPATH GRTAGS diff --git a/backend/WebUI/api_webui.go b/backend/WebUI/api_webui.go index 8d1b24be..01410334 100644 --- a/backend/WebUI/api_webui.go +++ b/backend/WebUI/api_webui.go @@ -1,12 +1,14 @@ package WebUI import ( + "context" "crypto/tls" "encoding/json" "fmt" "net/http" "os" "reflect" + "strconv" "strings" "time" @@ -24,15 +26,17 @@ import ( ) const ( - authSubsDataColl = "subscriptionData.authenticationData.authenticationSubscription" - amDataColl = "subscriptionData.provisionedData.amData" - smDataColl = "subscriptionData.provisionedData.smData" - smfSelDataColl = "subscriptionData.provisionedData.smfSelectionSubscriptionData" - amPolicyDataColl = "policyData.ues.amData" - smPolicyDataColl = "policyData.ues.smData" - flowRuleDataColl = "policyData.ues.flowRule" - userDataColl = "userData" - tenantDataColl = "tenantData" + authSubsDataColl = "subscriptionData.authenticationData.authenticationSubscription" + amDataColl = "subscriptionData.provisionedData.amData" + smDataColl = "subscriptionData.provisionedData.smData" + smfSelDataColl = "subscriptionData.provisionedData.smfSelectionSubscriptionData" + amPolicyDataColl = "policyData.ues.amData" + smPolicyDataColl = "policyData.ues.smData" + flowRuleDataColl = "policyData.ues.flowRule" + qosFlowDataColl = "policyData.ues.qosFlow" + userDataColl = "userData" + tenantDataColl = "tenantData" + msisdnSupiMapColl = "subscriptionData.msisdnSupiMap" ) var httpsClient *http.Client @@ -46,24 +50,30 @@ func init() { } func mapToByte(data map[string]interface{}) (ret []byte) { - ret, _ = json.Marshal(data) + ret, err := json.Marshal(data) + if err != nil { + logger.ProcLog.Errorf("mapToByte err: %+v", err) + } return } func sliceToByte(data []map[string]interface{}) (ret []byte) { - ret, _ = json.Marshal(data) + ret, err := json.Marshal(data) + if err != nil { + logger.ProcLog.Errorf("sliceToByte err: %+v", err) + } return } func toBsonM(data interface{}) (ret bson.M) { - tmp, _ := json.Marshal(data) - json.Unmarshal(tmp, &ret) - return -} - -func toBsonA(data interface{}) (ret bson.A) { - tmp, _ := json.Marshal(data) - json.Unmarshal(tmp, &ret) + tmp, err := json.Marshal(data) + if err != nil { + logger.ProcLog.Errorf("toBsonM err: %+v", err) + } + err = json.Unmarshal(tmp, &ret) + if err != nil { + logger.ProcLog.Errorf("toBsonM err: %+v", err) + } return } @@ -78,13 +88,52 @@ func UnescapeDnn(dnnKey string) string { func setCorsHeader(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") + c.Writer.Header().Set( + "Access-Control-Allow-Headers", + "Content-Type, Content-Length, Accept-Encoding, "+ + "X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With", + ) c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, PATCH, DELETE") } +func getMsisdn(gpsis interface{}) string { + msisdn := "" + gpsisReflected := reflect.ValueOf(gpsis) // use reflect to range over interface{} + for i := 0; i < gpsisReflected.Len(); i++ { + gpsi := gpsisReflected.Index(i).Interface().(string) // transform type reflect.value to string + if strings.HasPrefix(gpsi, "msisdn-") { // check if gpsi contain prefix "msisdn-" + msisdn = gpsi[7:] + } + } + return msisdn +} + +func msisdnToSupi(ueId string) string { + if strings.HasPrefix(ueId, "msisdn-") { + filter := bson.M{"msisdn": ueId[7:]} + dbResult, err := mongoapi.RestfulAPIGetOne(msisdnSupiMapColl, filter) + if err != nil { + logger.ProcLog.Errorf("GetSupibyMsisdn err: %+v", err) + } + if dbResult != nil { + ueId = dbResult["ueId"].(string) + } else { + // db cannot find a supi mapped to msisdn, return null string for error detection + logger.ProcLog.Error("msisdn not found") + return "" + } + } + return ueId +} + func sendResponseToClient(c *gin.Context, response *http.Response) { var jsonData interface{} - json.NewDecoder(response.Body).Decode(&jsonData) + err := json.NewDecoder(response.Body).Decode(&jsonData) + if err != nil { + logger.ProcLog.Errorf("sendResponseToClient err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } c.JSON(response.StatusCode, jsonData) } @@ -93,7 +142,9 @@ func sendResponseToClientFilterTenant(c *gin.Context, response *http.Response, t filterTenantIdOnly := bson.M{"tenantId": tenantId} amDataList, err := mongoapi.RestfulAPIGetMany(amDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("sendResponseToClientFilterTenant err: %+v", err) + logger.ProcLog.Errorf("sendResponseToClientFilterTenant err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } tenantCheck := func(supi string) bool { @@ -107,7 +158,12 @@ func sendResponseToClientFilterTenant(c *gin.Context, response *http.Response, t // Response data. var jsonData interface{} - json.NewDecoder(response.Body).Decode(&jsonData) + err = json.NewDecoder(response.Body).Decode(&jsonData) + if err != nil { + logger.ProcLog.Errorf("sendResponseToClientFilterTenant err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } s := reflect.ValueOf(jsonData) if s.Kind() != reflect.Slice { @@ -135,7 +191,7 @@ func sendResponseToClientFilterTenant(c *gin.Context, response *http.Response, t func GetSampleJSON(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get a JSON Example") + logger.ProcLog.Infoln("Get a JSON Example") var subsData SubsData @@ -345,14 +401,22 @@ func JWT(email, userId, tenantId string) string { claims["email"] = email claims["tenantId"] = tenantId - tokenString, _ := token.SignedString([]byte(os.Getenv("SIGNINGKEY"))) + tokenString, err := token.SignedString([]byte(os.Getenv("SIGNINGKEY"))) + if err != nil { + logger.ProcLog.Errorf("JWT err: %+v", err) + } return tokenString } -func generateHash(password string) { - hash, _ := bcrypt.GenerateFromPassword([]byte(password), 12) - logger.WebUILog.Warnln("Password hash:", hash) +func generateHash(password string) error { + hash, err := bcrypt.GenerateFromPassword([]byte(password), 12) + if err != nil { + logger.ProcLog.Errorf("generateHash err: %+v", err) + return err + } + logger.ProcLog.Warnln("Password hash:", hash) + return err } func Login(c *gin.Context) { @@ -361,21 +425,28 @@ func Login(c *gin.Context) { login := LoginRequest{} err := json.NewDecoder(c.Request.Body).Decode(&login) if err != nil { - logger.WebUILog.Warnln("JSON decode error", err) + logger.ProcLog.Warnln("JSON decode error", err) c.JSON(http.StatusInternalServerError, gin.H{}) return } - generateHash(login.Password) + err = generateHash(login.Password) + if err != nil { + logger.ProcLog.Errorf("Login err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } filterEmail := bson.M{"email": login.Username} userData, err := mongoapi.RestfulAPIGetOne(userDataColl, filterEmail) if err != nil { - logger.WebUILog.Errorf("Login err: %+v", err) + logger.ProcLog.Errorf("Login err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userData) == 0 { - logger.WebUILog.Warnln("Can't find user email", login.Username) + logger.ProcLog.Warnln("Can't find user email", login.Username) c.JSON(http.StatusForbidden, gin.H{}) return } @@ -384,7 +455,7 @@ func Login(c *gin.Context) { err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(login.Password)) if err != nil { - logger.WebUILog.Warnln("Password incorrect", login.Username) + logger.ProcLog.Warnln("Password incorrect", login.Username) c.JSON(http.StatusForbidden, gin.H{}) return } @@ -392,12 +463,12 @@ func Login(c *gin.Context) { userId := userData["userId"].(string) tenantId := userData["tenantId"].(string) - logger.WebUILog.Warnln("Login success", login.Username) - logger.WebUILog.Warnln("userid", userId) - logger.WebUILog.Warnln("tenantid", tenantId) + logger.ProcLog.Warnln("Login success", login.Username) + logger.ProcLog.Warnln("userid", userId) + logger.ProcLog.Warnln("tenantid", tenantId) token := JWT(login.Username, userId, tenantId) - logger.WebUILog.Warnln("token", token) + logger.ProcLog.Warnln("token", token) oauth := OAuth{} oauth.AccessToken = token @@ -421,7 +492,6 @@ func ParseJWT(tokenStr string) (jwt.MapClaims, error) { token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) { return []byte(os.Getenv("SIGNINGKEY")), nil }) - if err != nil { return nil, errors.Wrap(err, "ParseJWT error") } @@ -441,7 +511,7 @@ func CheckAuth(c *gin.Context) bool { } } -// Tenat ID +// Tenant ID func GetTenantId(c *gin.Context) (string, error) { tokenStr := c.GetHeader("Token") if tokenStr == "admin" { @@ -449,6 +519,7 @@ func GetTenantId(c *gin.Context) (string, error) { } claims, err := ParseJWT(tokenStr) if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{}) return "", errors.Wrap(err, "GetTenantId error") } return claims["tenantId"].(string), nil @@ -465,11 +536,17 @@ func GetTenants(c *gin.Context) { tenantDataInterface, err := mongoapi.RestfulAPIGetMany(tenantDataColl, bson.M{}) if err != nil { - logger.WebUILog.Errorf("GetTenants err: %+v", err) + logger.ProcLog.Errorf("GetTenants err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var tenantData []Tenant - json.Unmarshal(sliceToByte(tenantDataInterface), &tenantData) - + err = json.Unmarshal(sliceToByte(tenantDataInterface), &tenantData) + if err != nil { + logger.ProcLog.Errorf("GetTenants err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } c.JSON(http.StatusOK, tenantData) } @@ -486,7 +563,9 @@ func GetTenantByID(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} tenantDataInterface, err := mongoapi.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("GetTenantByID err: %+v", err) + logger.ProcLog.Errorf("GetTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(tenantDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -494,8 +573,12 @@ func GetTenantByID(c *gin.Context) { } var tenantData Tenant - json.Unmarshal(mapToByte(tenantDataInterface), &tenantData) - + err = json.Unmarshal(mapToByte(tenantDataInterface), &tenantData) + if err != nil { + logger.ProcLog.Errorf("GetTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } c.JSON(http.StatusOK, tenantData) } @@ -520,7 +603,9 @@ func PostTenant(c *gin.Context) { tenantBsonM := toBsonM(tenantData) filterTenantIdOnly := bson.M{"tenantId": tenantData.TenantId} if _, err := mongoapi.RestfulAPIPost(tenantDataColl, filterTenantIdOnly, tenantBsonM); err != nil { - logger.WebUILog.Errorf("PostTenant err: %+v", err) + logger.ProcLog.Errorf("PostTenant err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, tenantData) @@ -539,7 +624,9 @@ func PutTenantByID(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} tenantDataInterface, err := mongoapi.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("PutTenantByID err: %+v", err) + logger.ProcLog.Errorf("PutTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(tenantDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -556,7 +643,9 @@ func PutTenantByID(c *gin.Context) { tenantBsonM := toBsonM(tenantData) filterTenantIdOnly = bson.M{"tenantId": tenantId} if _, err := mongoapi.RestfulAPIPost(tenantDataColl, filterTenantIdOnly, tenantBsonM); err != nil { - logger.WebUILog.Errorf("PutTenantByID err: %+v", err) + logger.ProcLog.Errorf("PutTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, gin.H{}) @@ -574,13 +663,19 @@ func DeleteTenantByID(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} if err := mongoapi.RestfulAPIDeleteMany(amDataColl, filterTenantIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteTenantByID err: %+v", err) + logger.ProcLog.Errorf("DeleteTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIDeleteMany(userDataColl, filterTenantIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteTenantByID err: %+v", err) + logger.ProcLog.Errorf("DeleteTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIDeleteOne(tenantDataColl, filterTenantIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteTenantByID err: %+v", err) + logger.ProcLog.Errorf("DeleteTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, gin.H{}) @@ -591,7 +686,7 @@ func GetTenantById(tenantId string) map[string]interface{} { filterTenantIdOnly := bson.M{"tenantId": tenantId} tenantData, err := mongoapi.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("GetTenantById err: %+v", err) + logger.ProcLog.Errorf("GetTenantById err: %+v", err) return nil } return tenantData @@ -615,12 +710,20 @@ func GetUsers(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} userDataInterface, err := mongoapi.RestfulAPIGetMany(userDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("GetUsers err: %+v", err) + logger.ProcLog.Errorf("GetUsers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var userData []User - json.Unmarshal(sliceToByte(userDataInterface), &userData) - for pos, _ := range userData { + err = json.Unmarshal(sliceToByte(userDataInterface), &userData) + if err != nil { + logger.ProcLog.Errorf("GetUsers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + + for pos := range userData { userData[pos].EncryptedPassword = "" } @@ -645,7 +748,9 @@ func GetUserByID(c *gin.Context) { filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId} userDataInterface, err := mongoapi.RestfulAPIGetOne(userDataColl, filterUserIdOnly) if err != nil { - logger.WebUILog.Errorf("GetUserByID err: %+v", err) + logger.ProcLog.Errorf("GetUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -653,7 +758,13 @@ func GetUserByID(c *gin.Context) { } var userData User - json.Unmarshal(mapToByte(userDataInterface), &userData) + err = json.Unmarshal(mapToByte(userDataInterface), &userData) + if err != nil { + logger.ProcLog.Errorf("GetUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + userData.EncryptedPassword = "" c.JSON(http.StatusOK, userData) @@ -682,23 +793,32 @@ func PostUserByID(c *gin.Context) { filterEmail := bson.M{"email": userData.Email} userWithEmailData, err := mongoapi.RestfulAPIGetOne(userDataColl, filterEmail) if err != nil { - logger.WebUILog.Errorf("PostUserByID err: %+v", err) + logger.ProcLog.Errorf("PostUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userWithEmailData) != 0 { - logger.WebUILog.Warnln("Email already exists", userData.Email) + logger.ProcLog.Warnln("Email already exists", userData.Email) c.JSON(http.StatusForbidden, gin.H{}) return } userData.TenantId = tenantId userData.UserId = uuid.Must(uuid.NewRandom()).String() - hash, _ := bcrypt.GenerateFromPassword([]byte(userData.EncryptedPassword), 12) + hash, err := bcrypt.GenerateFromPassword([]byte(userData.EncryptedPassword), 12) + if err != nil { + logger.ProcLog.Errorf("PostUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } userData.EncryptedPassword = string(hash) userBsonM := toBsonM(userData) filterUserIdOnly := bson.M{"tenantId": userData.TenantId, "userId": userData.UserId} if _, err := mongoapi.RestfulAPIPost(userDataColl, filterUserIdOnly, userBsonM); err != nil { - logger.WebUILog.Errorf("PostUserByID err: %+v", err) + logger.ProcLog.Errorf("PostUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, userData) @@ -728,7 +848,9 @@ func PutUserByID(c *gin.Context) { filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId} userDataInterface, err := mongoapi.RestfulAPIGetOne(userDataColl, filterUserIdOnly) if err != nil { - logger.WebUILog.Errorf("PutUserByID err: %+v", err) + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -736,13 +858,20 @@ func PutUserByID(c *gin.Context) { } var userData User - json.Unmarshal(mapToByte(userDataInterface), &userData) + err = json.Unmarshal(mapToByte(userDataInterface), &userData) + if err != nil { + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } if newUserData.Email != "" && newUserData.Email != userData.Email { filterEmail := bson.M{"email": newUserData.Email} sameEmailInterface, err := mongoapi.RestfulAPIGetOne(userDataColl, filterEmail) if err != nil { - logger.WebUILog.Errorf("PutUserByID err: %+v", err) + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(sameEmailInterface) != 0 { c.JSON(http.StatusBadRequest, bson.M{}) @@ -752,13 +881,20 @@ func PutUserByID(c *gin.Context) { } if newUserData.EncryptedPassword != "" { - hash, _ := bcrypt.GenerateFromPassword([]byte(newUserData.EncryptedPassword), 12) + hash, err := bcrypt.GenerateFromPassword([]byte(newUserData.EncryptedPassword), 12) + if err != nil { + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } userData.EncryptedPassword = string(hash) } userBsonM := toBsonM(userData) if _, err := mongoapi.RestfulAPIPost(userDataColl, filterUserIdOnly, userBsonM); err != nil { - logger.WebUILog.Errorf("PutUserByID err: %+v", err) + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, userData) @@ -781,7 +917,9 @@ func DeleteUserByID(c *gin.Context) { filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId} if err := mongoapi.RestfulAPIDeleteOne(userDataColl, filterUserIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteUserByID err: %+v", err) + logger.ProcLog.Errorf("DeleteUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, gin.H{}) @@ -791,7 +929,7 @@ func DeleteUserByID(c *gin.Context) { func GetSubscribers(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get All Subscribers List") + logger.ProcLog.Infoln("Get All Subscribers List") tokenStr := c.GetHeader("Token") @@ -801,7 +939,7 @@ func GetSubscribers(c *gin.Context) { claims, err = ParseJWT(tokenStr) } if err != nil { - logger.WebUILog.Errorln(err.Error()) + logger.ProcLog.Errorln(err.Error()) c.JSON(http.StatusBadRequest, gin.H{ "cause": "Illegal Token", }) @@ -811,26 +949,37 @@ func GetSubscribers(c *gin.Context) { var subsList []SubsListIE = make([]SubsListIE, 0) amDataList, err := mongoapi.RestfulAPIGetMany(amDataColl, bson.M{}) if err != nil { - logger.WebUILog.Errorf("GetSubscribers err: %+v", err) + logger.ProcLog.Errorf("GetSubscribers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } for _, amData := range amDataList { ueId := amData["ueId"] servingPlmnId := amData["servingPlmnId"] + msisdn := getMsisdn(amData["gpsis"]) tenantId := amData["tenantId"] filterUeIdOnly := bson.M{"ueId": ueId} authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscribers err: %+v", err) + logger.ProcLog.Errorf("GetSubscribers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var authSubsData AuthSub - json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData) + err = json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData) + if err != nil { + logger.ProcLog.Errorf("GetSubscribers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } if tokenStr == "admin" || tenantId == claims["tenantId"].(string) { tmp := SubsListIE{ PlmnID: servingPlmnId.(string), UeId: ueId.(string), + Msisdn: msisdn, } subsList = append(subsList, tmp) } @@ -842,59 +991,128 @@ func GetSubscribers(c *gin.Context) { func GetSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get One Subscriber Data") + logger.ProcLog.Infoln("Get One Subscriber Data") var subsData SubsData ueId := c.Param("ueId") + ueId = msisdnToSupi(ueId) servingPlmnId := c.Param("servingPlmnId") - + // checking whether msisdn is successfully transformed to supi or not + if ueId == "" { + logger.ProcLog.Errorf("GetSubscriberByID err: msisdn does not exists") + c.JSON(http.StatusNotFound, gin.H{ + "cause": "msisdn does not exists", + }) + return + } filterUeIdOnly := bson.M{"ueId": ueId} filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } amDataDataInterface, err := mongoapi.RestfulAPIGetOne(amDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } smDataDataInterface, err := mongoapi.RestfulAPIGetMany(smDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } smfSelDataInterface, err := mongoapi.RestfulAPIGetOne(smfSelDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } amPolicyDataInterface, err := mongoapi.RestfulAPIGetOne(amPolicyDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } smPolicyDataInterface, err := mongoapi.RestfulAPIGetOne(smPolicyDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } flowRuleDataInterface, err := mongoapi.RestfulAPIGetMany(flowRuleDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + qosFlowInterface, err := mongoapi.RestfulAPIGetMany(qosFlowDataColl, filter) + if err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var authSubsData models.AuthenticationSubscription - json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData) + if err := json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var amDataData models.AccessAndMobilitySubscriptionData - json.Unmarshal(mapToByte(amDataDataInterface), &amDataData) + if err := json.Unmarshal(mapToByte(amDataDataInterface), &amDataData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var smDataData []models.SessionManagementSubscriptionData - json.Unmarshal(sliceToByte(smDataDataInterface), &smDataData) + if err := json.Unmarshal(sliceToByte(smDataDataInterface), &smDataData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var smfSelData models.SmfSelectionSubscriptionData - json.Unmarshal(mapToByte(smfSelDataInterface), &smfSelData) + if err := json.Unmarshal(mapToByte(smfSelDataInterface), &smfSelData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var amPolicyData models.AmPolicyData - json.Unmarshal(mapToByte(amPolicyDataInterface), &amPolicyData) + if err := json.Unmarshal(mapToByte(amPolicyDataInterface), &amPolicyData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var smPolicyData models.SmPolicyData - json.Unmarshal(mapToByte(smPolicyDataInterface), &smPolicyData) + if err := json.Unmarshal(mapToByte(smPolicyDataInterface), &smPolicyData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var flowRules []FlowRule - json.Unmarshal(sliceToByte(flowRuleDataInterface), &flowRules) + if err := json.Unmarshal(sliceToByte(flowRuleDataInterface), &flowRules); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + var qosFlows []QosFlow + if err := json.Unmarshal(sliceToByte(qosFlowInterface), &qosFlows); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + + if flowRules == nil { + flowRules = make([]FlowRule, 0) + } + if qosFlows == nil { + qosFlows = make([]QosFlow, 0) + } for key, SnssaiData := range smPolicyData.SmPolicySnssaiData { tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) @@ -916,15 +1134,27 @@ func GetSubscriberByID(c *gin.Context) { AmPolicyData: amPolicyData, SmPolicyData: smPolicyData, FlowRules: flowRules, + QosFlows: qosFlows, } c.JSON(http.StatusOK, subsData) } // Post subscriber by IMSI(ueId) and PlmnID(servingPlmnId) +// PostSubscriberByID godoc +// @Summary CreateSubscriberByID +// @Description Create subscriber by IMSI(ueId) and PlmnID(servingPlmnId) +// @Accept json +// @Produce json +// @Param ueId path string true "imsi" +// @Param servingPlmnId path string true "servingPlmnId" +// @Param subdata body SubsData true "sub data" +// @Success 201 "Create subscription success" +// @Failure 400 {object} HTTPError "JSON format incorrect" +// @Router /subscriber/{ueId}/{servingPlmnId}/{userNumber} [post] func PostSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Post One Subscriber Data") + logger.ProcLog.Infoln("Post One Subscriber Data") var claims jwt.MapClaims = nil var err error = nil @@ -934,7 +1164,7 @@ func PostSubscriberByID(c *gin.Context) { claims, err = ParseJWT(tokenStr) } if err != nil { - logger.WebUILog.Errorln(err.Error()) + logger.ProcLog.Errorln(err.Error()) c.JSON(http.StatusBadRequest, gin.H{ "cause": "Illegal Token", }) @@ -942,205 +1172,295 @@ func PostSubscriberByID(c *gin.Context) { } var subsData SubsData - if err := c.ShouldBindJSON(&subsData); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %v", err) + err = c.ShouldBindJSON(&subsData) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %v", err) c.JSON(http.StatusBadRequest, gin.H{ "cause": "JSON format incorrect", }) return } - - ueId := c.Param("ueId") + ueId := strings.Split(c.Param("ueId"), "-")[1] servingPlmnId := c.Param("servingPlmnId") + userNumber := c.Param("userNumber") + if userNumber == "" { + userNumber = "1" + } + userNumberTemp, err := strconv.Atoi(userNumber) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "userNumber format incorrect", + }) + return + } + msisdn := getMsisdn(toBsonM(subsData.AccessAndMobilitySubscriptionData)["gpsis"]) + msisdnTemp := 0 + if msisdn != "" { + msisdnTemp, err = strconv.Atoi(msisdn) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "msisdn format incorrect", + }) + return + } + } - filterUeIdOnly := bson.M{"ueId": ueId} - filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} + ueIdTemp, err := strconv.Atoi(ueId) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "ueId format incorrect", + }) + return + } - // Lookup same UE ID of other tenant's subscription. - if claims != nil { - authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) - if err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) + for i := 0; i < userNumberTemp; i++ { + ueId = fmt.Sprintf("imsi-%015d", ueIdTemp) + if msisdnTemp != 0 { + if !validate(ueId, msisdn) { + logger.ProcLog.Errorf("duplicate msisdn: %v", msisdn) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "duplicate msisdn", + }) + return + } + msisdnTemp += 1 } - if len(authSubsDataInterface) > 0 { - if authSubsDataInterface["tenantId"].(string) != claims["tenantId"].(string) { - c.JSON(http.StatusUnprocessableEntity, gin.H{}) + ueIdTemp += 1 + + subsData.AccessAndMobilitySubscriptionData.Gpsis[0] = "msisdn-" + msisdn + // create a msisdn-supi map + logger.ProcLog.Infof("PostSubscriberByID msisdn: %+v", msisdn) + msisdnSupiMapOperation(ueId, msisdn, "post") + filterUeIdOnly := bson.M{"ueId": ueId} + + // Lookup same UE ID of other tenant's subscription. + if claims != nil { + authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) return } + if len(authSubsDataInterface) > 0 { + if authSubsDataInterface["tenantId"].(string) != claims["tenantId"].(string) { + c.JSON(http.StatusUnprocessableEntity, gin.H{}) + return + } + } } + dbOperation(ueId, servingPlmnId, "post", &subsData, claims) } + c.JSON(http.StatusCreated, gin.H{}) +} - authSubsBsonM := toBsonM(subsData.AuthenticationSubscription) - authSubsBsonM["ueId"] = ueId - if claims != nil { - authSubsBsonM["tenantId"] = claims["tenantId"].(string) +func validate(supi string, msisdn string) bool { + filter := bson.M{"msisdn": msisdn} + msisdnSupiMap, err := mongoapi.RestfulAPIGetOne(msisdnSupiMapColl, filter) + if err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) } - amDataBsonM := toBsonM(subsData.AccessAndMobilitySubscriptionData) - amDataBsonM["ueId"] = ueId - amDataBsonM["servingPlmnId"] = servingPlmnId - if claims != nil { - amDataBsonM["tenantId"] = claims["tenantId"].(string) + if msisdnSupiMap != nil && msisdnSupiMap["ueId"] != supi { + return false + } else { + return true } +} - smDatasBsonA := make([]interface{}, 0, len(subsData.SessionManagementSubscriptionData)) - for _, smSubsData := range subsData.SessionManagementSubscriptionData { - smDataBsonM := toBsonM(smSubsData) - smDataBsonM["ueId"] = ueId - smDataBsonM["servingPlmnId"] = servingPlmnId - smDatasBsonA = append(smDatasBsonA, smDataBsonM) - } +func msisdnSupiMapOperation(supi string, msisdn string, method string) { + filter := bson.M{"ueId": supi} + data := bson.M{"ueId": supi, "msisdn": msisdn} - for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { - tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) - for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { - escapedDnn := EscapeDnn(dnnKey) - tmpSmPolicyDnnData[escapedDnn] = dnn + if method == "put" || method == "post" { + if msisdn != "" { + if _, err := mongoapi.RestfulAPIPutOne(msisdnSupiMapColl, filter, data); err != nil { + logger.ProcLog.Errorf("PutMsisdnSupiMap err: %+v", err) + } + } else { + // delete + if err := mongoapi.RestfulAPIDeleteOne(msisdnSupiMapColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteMsisdnSupiMap err: %+v", err) + } } - SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData - subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData } +} - smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) - smfSelSubsBsonM["ueId"] = ueId - smfSelSubsBsonM["servingPlmnId"] = servingPlmnId - amPolicyDataBsonM := toBsonM(subsData.AmPolicyData) - amPolicyDataBsonM["ueId"] = ueId - smPolicyDataBsonM := toBsonM(subsData.SmPolicyData) - smPolicyDataBsonM["ueId"] = ueId +func dbOperation(ueId string, servingPlmnId string, method string, subsData *SubsData, claims jwt.MapClaims) { + filterUeIdOnly := bson.M{"ueId": ueId} + filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} - flowRulesBsonA := make([]interface{}, 0, len(subsData.FlowRules)) - for _, flowRule := range subsData.FlowRules { - flowRuleBsonM := toBsonM(flowRule) - flowRuleBsonM["ueId"] = ueId - flowRuleBsonM["servingPlmnId"] = servingPlmnId - flowRulesBsonA = append(flowRulesBsonA, flowRuleBsonM) + // Replace all data with new one + if method == "put" { + if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(qosFlowDataColl, filter); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } else if method == "delete" { + if err := mongoapi.RestfulAPIDeleteOne(authSubsDataColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(amDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(smfSelDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(amPolicyDataColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(smPolicyDataColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(qosFlowDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(msisdnSupiMapColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteMsisdnSupiMap err: %+v", err) + } } + if method == "post" || method == "put" { + authSubsBsonM := toBsonM(subsData.AuthenticationSubscription) + authSubsBsonM["ueId"] = ueId + if claims != nil { + authSubsBsonM["tenantId"] = claims["tenantId"].(string) + } + amDataBsonM := toBsonM(subsData.AccessAndMobilitySubscriptionData) + amDataBsonM["ueId"] = ueId + amDataBsonM["servingPlmnId"] = servingPlmnId + if claims != nil { + amDataBsonM["tenantId"] = claims["tenantId"].(string) + } - if _, err := mongoapi.RestfulAPIPost(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(amDataColl, filter, amDataBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIPostMany(smDataColl, filter, smDatasBsonA); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIPostMany(flowRuleDataColl, filter, flowRulesBsonA); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } + // Replace all data with new one + if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + for _, data := range subsData.SessionManagementSubscriptionData { + smDataBsonM := toBsonM(data) + smDataBsonM["ueId"] = ueId + smDataBsonM["servingPlmnId"] = servingPlmnId + filterSmData := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId, "snssai": data.SingleNssai} + if _, err := mongoapi.RestfulAPIPutOne(smDataColl, filterSmData, smDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } - c.JSON(http.StatusCreated, gin.H{}) + for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { + tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) + for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { + escapedDnn := EscapeDnn(dnnKey) + tmpSmPolicyDnnData[escapedDnn] = dnn + } + SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData + subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData + } + + smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) + smfSelSubsBsonM["ueId"] = ueId + smfSelSubsBsonM["servingPlmnId"] = servingPlmnId + amPolicyDataBsonM := toBsonM(subsData.AmPolicyData) + amPolicyDataBsonM["ueId"] = ueId + smPolicyDataBsonM := toBsonM(subsData.SmPolicyData) + smPolicyDataBsonM["ueId"] = ueId + + if len(subsData.FlowRules) == 0 { + logger.ProcLog.Infoln("No Flow Rule") + } else { + flowRulesBsonA := make([]interface{}, 0, len(subsData.FlowRules)) + for _, flowRule := range subsData.FlowRules { + flowRuleBsonM := toBsonM(flowRule) + flowRuleBsonM["ueId"] = ueId + flowRuleBsonM["servingPlmnId"] = servingPlmnId + flowRulesBsonA = append(flowRulesBsonA, flowRuleBsonM) + } + if err := mongoapi.RestfulAPIPostMany(flowRuleDataColl, filter, flowRulesBsonA); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } + + if len(subsData.QosFlows) == 0 { + logger.ProcLog.Infoln("No QoS Flow") + } else { + qosFlowBsonA := make([]interface{}, 0, len(subsData.QosFlows)) + for _, qosFlow := range subsData.QosFlows { + qosFlowBsonM := toBsonM(qosFlow) + qosFlowBsonM["ueId"] = ueId + qosFlowBsonM["servingPlmnId"] = servingPlmnId + qosFlowBsonA = append(qosFlowBsonA, qosFlowBsonM) + } + if err := mongoapi.RestfulAPIPostMany(qosFlowDataColl, filter, qosFlowBsonA); err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + } + } + + if _, err := mongoapi.RestfulAPIPutOne(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(amDataColl, filter, amDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } } // Put subscriber by IMSI(ueId) and PlmnID(servingPlmnId) func PutSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Put One Subscriber Data") - + logger.ProcLog.Infoln("Put One Subscriber Data") var subsData SubsData if err := c.ShouldBindJSON(&subsData); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %v", err) + logger.ProcLog.Errorf("PutSubscriberByID err: %v", err) c.JSON(http.StatusBadRequest, gin.H{ "cause": "JSON format incorrect", }) return } - ueId := c.Param("ueId") servingPlmnId := c.Param("servingPlmnId") - - filterUeIdOnly := bson.M{"ueId": ueId} - filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} - - authSubsBsonM := toBsonM(subsData.AuthenticationSubscription) - authSubsBsonM["ueId"] = ueId - amDataBsonM := toBsonM(subsData.AccessAndMobilitySubscriptionData) - amDataBsonM["ueId"] = ueId - amDataBsonM["servingPlmnId"] = servingPlmnId - - // Replace all data with new one - if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - for _, data := range subsData.SessionManagementSubscriptionData { - smDataBsonM := toBsonM(data) - smDataBsonM["ueId"] = ueId - smDataBsonM["servingPlmnId"] = servingPlmnId - filterSmData := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId, "snssai": data.SingleNssai} - if _, err := mongoapi.RestfulAPIPutOne(smDataColl, filterSmData, smDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - } - - for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { - tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) - for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { - escapedDnn := EscapeDnn(dnnKey) - tmpSmPolicyDnnData[escapedDnn] = dnn - } - SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData - subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData - } - - smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) - smfSelSubsBsonM["ueId"] = ueId - smfSelSubsBsonM["servingPlmnId"] = servingPlmnId - amPolicyDataBsonM := toBsonM(subsData.AmPolicyData) - amPolicyDataBsonM["ueId"] = ueId - smPolicyDataBsonM := toBsonM(subsData.SmPolicyData) - smPolicyDataBsonM["ueId"] = ueId - - flowRulesBsonA := make([]interface{}, 0, len(subsData.FlowRules)) - for _, flowRule := range subsData.FlowRules { - flowRuleBsonM := toBsonM(flowRule) - flowRuleBsonM["ueId"] = ueId - flowRuleBsonM["servingPlmnId"] = servingPlmnId - flowRulesBsonA = append(flowRulesBsonA, flowRuleBsonM) - } - // Replace all data with new one - if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIPostMany(flowRuleDataColl, filter, flowRulesBsonA); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) + // modify a msisdn-supi map + msisdn := getMsisdn(toBsonM(subsData.AccessAndMobilitySubscriptionData)["gpsis"]) + if !validate(ueId, msisdn) { + logger.ProcLog.Errorf("duplicate msisdn: %v", msisdn) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "duplicate msisdn", + }) + return } - if _, err := mongoapi.RestfulAPIPutOne(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(amDataColl, filter, amDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } + logger.ProcLog.Infof("PutSubscriberByID msisdn: %+v", msisdn) + msisdnSupiMapOperation(ueId, msisdn, "put") + var claims jwt.MapClaims = nil + dbOperation(ueId, servingPlmnId, "put", &subsData, claims) c.JSON(http.StatusNoContent, gin.H{}) } // Patch subscriber by IMSI(ueId) and PlmnID(servingPlmnId) func PatchSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Patch One Subscriber Data") + logger.ProcLog.Infoln("Patch One Subscriber Data") var subsData SubsData if err := c.ShouldBindJSON(&subsData); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %v", err) c.JSON(http.StatusBadRequest, gin.H{ "cause": "JSON format incorrect", }) @@ -1148,8 +1468,16 @@ func PatchSubscriberByID(c *gin.Context) { } ueId := c.Param("ueId") + ueId = msisdnToSupi(ueId) servingPlmnId := c.Param("servingPlmnId") - + // checking whether msisdn is successfully transformed to supi or not + if ueId == "" { + logger.ProcLog.Errorf("PatchSubscriberByID err: msisdn does not exists") + c.JSON(http.StatusNotFound, gin.H{ + "cause": "msisdn does not exists", + }) + return + } filterUeIdOnly := bson.M{"ueId": ueId} filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} @@ -1161,7 +1489,9 @@ func PatchSubscriberByID(c *gin.Context) { // Replace all data with new one if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } for _, data := range subsData.SessionManagementSubscriptionData { smDataBsonM := toBsonM(data) @@ -1169,18 +1499,10 @@ func PatchSubscriberByID(c *gin.Context) { smDataBsonM["servingPlmnId"] = servingPlmnId filterSmData := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId, "snssai": data.SingleNssai} if err := mongoapi.RestfulAPIMergePatch(smDataColl, filterSmData, smDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) - } - } - - for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { - tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) - for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { - escapedDnn := EscapeDnn(dnnKey) - tmpSmPolicyDnnData[escapedDnn] = dnn + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } - SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData - subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData } smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) @@ -1192,19 +1514,29 @@ func PatchSubscriberByID(c *gin.Context) { smPolicyDataBsonM["ueId"] = ueId if err := mongoapi.RestfulAPIMergePatch(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(amDataColl, filter, amDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusNoContent, gin.H{}) @@ -1213,45 +1545,29 @@ func PatchSubscriberByID(c *gin.Context) { // Delete subscriber by IMSI(ueId) and PlmnID(servingPlmnId) func DeleteSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Delete One Subscriber Data") - + logger.ProcLog.Infoln("Delete One Subscriber Data") ueId := c.Param("ueId") + ueId = msisdnToSupi(ueId) servingPlmnId := c.Param("servingPlmnId") - - filterUeIdOnly := bson.M{"ueId": ueId} - filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} - - if err := mongoapi.RestfulAPIDeleteOne(authSubsDataColl, filterUeIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(amDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(smfSelDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(amPolicyDataColl, filterUeIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(smPolicyDataColl, filterUeIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) + // checking whether msisdn is successfully transformed to supi or not + if ueId == "" { + logger.ProcLog.Errorf("DeleteSubscriberByID err: msisdn does not exists") + c.JSON(http.StatusNotFound, gin.H{ + "cause": "msisdn does not exists", + }) + return } - + var claims jwt.MapClaims = nil + dbOperation(ueId, servingPlmnId, "delete", nil, claims) c.JSON(http.StatusNoContent, gin.H{}) } func GetRegisteredUEContext(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get Registered UE Context") + logger.ProcLog.Infoln("Get Registered UE Context") - webuiSelf := webui_context.WEBUI_Self() + webuiSelf := webui_context.GetSelf() webuiSelf.UpdateNfProfiles() supi, supiExists := c.Params.Get("supi") @@ -1266,17 +1582,28 @@ func GetRegisteredUEContext(c *gin.Context) { requestUri = fmt.Sprintf("%s/namf-oam/v1/registered-ue-context", amfUris[0]) } - resp, err := httpsClient.Get(requestUri) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, requestUri, nil) + if err != nil { + logger.ProcLog.Error(err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + resp, err := httpsClient.Do(req) if err != nil { - logger.WebUILog.Error(err) + logger.ProcLog.Error(err) c.JSON(http.StatusInternalServerError, gin.H{}) return } + defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + logger.ProcLog.Error(closeErr) + } + }() // Filter by tenant. tenantId, err := GetTenantId(c) if err != nil { - logger.WebUILog.Errorln(err.Error()) + logger.ProcLog.Errorln(err.Error()) c.JSON(http.StatusBadRequest, gin.H{ "cause": "Illegal Token", }) @@ -1298,9 +1625,9 @@ func GetRegisteredUEContext(c *gin.Context) { func GetUEPDUSessionInfo(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get UE PDU Session Info") + logger.ProcLog.Infoln("Get UE PDU Session Info") - webuiSelf := webui_context.WEBUI_Self() + webuiSelf := webui_context.GetSelf() webuiSelf.UpdateNfProfiles() smContextRef, smContextRefExists := c.Params.Get("smContextRef") @@ -1312,12 +1639,23 @@ func GetUEPDUSessionInfo(c *gin.Context) { // TODO: support fetching data from multiple SMF if smfUris := webuiSelf.GetOamUris(models.NfType_SMF); smfUris != nil { requestUri := fmt.Sprintf("%s/nsmf-oam/v1/ue-pdu-session-info/%s", smfUris[0], smContextRef) - resp, err := httpsClient.Get(requestUri) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, requestUri, nil) if err != nil { - logger.WebUILog.Error(err) + logger.ProcLog.Error(err) c.JSON(http.StatusInternalServerError, gin.H{}) return } + resp, err := httpsClient.Do(req) + if err != nil { + logger.ProcLog.Error(err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + logger.ProcLog.Error(closeErr) + } + }() sendResponseToClient(c, resp) } else { diff --git a/backend/WebUI/model_flow_rule.go b/backend/WebUI/model_flow_rule.go index e16bd44e..e6fa7d6c 100644 --- a/backend/WebUI/model_flow_rule.go +++ b/backend/WebUI/model_flow_rule.go @@ -1,12 +1,9 @@ package WebUI type FlowRule struct { - Filter string `json:"filter,omitempty" yaml:"filter" bson:"filter" mapstructure:"filter"` - Snssai string `json:"snssai,omitempty" yaml:"snssai" bson:"snssai" mapstructure:"snssai"` - Dnn string `json:"dnn,omitempty" yaml:"v" bson:"dnn" mapstructure:"dnn"` - Var5QI int `json:"5qi,omitempty" yaml:"5qi" bson:"5qi" mapstructure:"5qi"` - MBRUL string `json:"mbrUL,omitempty" yaml:"mbrUL" bson:"mbrUL" mapstructure:"mbrUL"` - MBRDL string `json:"mbrDL,omitempty" yaml:"mbrDL" bson:"mbrDL" mapstructure:"mbrDL"` - GBRUL string `json:"gbrUL,omitempty" yaml:"gbrUL" bson:"gbrUL" mapstructure:"gbrUL"` - GBRDL string `json:"gbrDL,omitempty" yaml:"gbrDL" bson:"gbrDL" mapstructure:"gbrDL"` + Filter string `json:"filter,omitempty" yaml:"filter" bson:"filter" mapstructure:"filter"` + Precedence int `json:"precedence,omitempty" yaml:"precedence" bson:"precedence" mapstructure:"precedence"` + Snssai string `json:"snssai,omitempty" yaml:"snssai" bson:"snssai" mapstructure:"snssai"` + Dnn string `json:"dnn,omitempty" yaml:"dnn" bson:"dnn" mapstructure:"dnn"` + QFI int `json:"qfi,omitempty" yaml:"qfi" bson:"qfi" mapstructure:"qfi"` } diff --git a/backend/WebUI/model_qos_flow.go b/backend/WebUI/model_qos_flow.go new file mode 100644 index 00000000..648cc417 --- /dev/null +++ b/backend/WebUI/model_qos_flow.go @@ -0,0 +1,12 @@ +package WebUI + +type QosFlow struct { + Snssai string `json:"snssai" yaml:"snssai" bson:"snssai" mapstructure:"snssai"` + Dnn string `json:"dnn" yaml:"dnn" bson:"dnn" mapstructure:"dnn"` + QFI uint8 `json:"qfi" yaml:"qfi" bson:"qfi" mapstructure:"qfi"` + Var5QI int `json:"5qi" yaml:"5qi" bson:"5qi" mapstructure:"5qi"` + MBRUL string `json:"mbrUL,omitempty" yaml:"mbrUL" bson:"mbrUL" mapstructure:"mbrUL"` + MBRDL string `json:"mbrDL,omitempty" yaml:"mbrDL" bson:"mbrDL" mapstructure:"mbrDL"` + GBRUL string `json:"gbrUL,omitempty" yaml:"gbrUL" bson:"gbrUL" mapstructure:"gbrUL"` + GBRDL string `json:"gbrDL,omitempty" yaml:"gbrDL" bson:"gbrDL" mapstructure:"gbrDL"` +} diff --git a/backend/WebUI/model_subs_data.go b/backend/WebUI/model_subs_data.go index 0a41309f..3efde2ad 100644 --- a/backend/WebUI/model_subs_data.go +++ b/backend/WebUI/model_subs_data.go @@ -14,4 +14,5 @@ type SubsData struct { AmPolicyData models.AmPolicyData `json:"AmPolicyData"` SmPolicyData models.SmPolicyData `json:"SmPolicyData"` FlowRules []FlowRule `json:"FlowRules"` + QosFlows []QosFlow `json:"QosFlows"` } diff --git a/backend/WebUI/model_subs_list_ie.go b/backend/WebUI/model_subs_list_ie.go index b4f3000c..5fd51c3b 100644 --- a/backend/WebUI/model_subs_list_ie.go +++ b/backend/WebUI/model_subs_list_ie.go @@ -3,4 +3,5 @@ package WebUI type SubsListIE struct { PlmnID string `json:"plmnID"` UeId string `json:"ueId"` + Msisdn string `json:"msisdn"` } diff --git a/backend/WebUI/routers.go b/backend/WebUI/routers.go index 226b0a36..a3ab212c 100644 --- a/backend/WebUI/routers.go +++ b/backend/WebUI/routers.go @@ -165,6 +165,13 @@ var routes = Routes{ PostSubscriberByID, }, + { + "PostMultiSubscriber", + http.MethodPost, + "/subscriber/:ueId/:servingPlmnId/:userNumber", + PostSubscriberByID, + }, + { "PutSubscriberByID", http.MethodPut, diff --git a/backend/factory/config.go b/backend/factory/config.go index 8ddbe212..5de44c58 100644 --- a/backend/factory/config.go +++ b/backend/factory/config.go @@ -7,32 +7,145 @@ package factory import ( - logger_util "github.com/free5gc/util/logger" + "fmt" + "sync" + + "github.com/asaskevich/govalidator" + + "github.com/free5gc/webconsole/backend/logger" +) + +const ( + WebuiDefaultTLSKeyLogPath = "./log/webuisslkey.log" + WebuiDefaultCertPemPath = "./cert/webui.pem" + WebuiDefaultPrivateKeyPath = "./cert/webui.key" + WebuiDefaultConfigPath = "./config/webuicfg.yaml" ) type Config struct { - Info *Info `yaml:"info"` - Configuration *Configuration `yaml:"configuration"` - Logger *logger_util.Logger `yaml:"logger"` + Info *Info `yaml:"info" valid:"required"` + Configuration *Configuration `yaml:"configuration" valid:"required"` + Logger *Logger `yaml:"logger" valid:"required"` + sync.RWMutex +} + +func (c *Config) Validate() (bool, error) { + result, err := govalidator.ValidateStruct(c) + return result, appendInvalid(err) } type Info struct { - Version string `yaml:"version,omitempty"` - Description string `yaml:"description,omitempty"` + Version string `yaml:"version,omitempty" valid:"required,in(1.0.1)"` + Description string `yaml:"description,omitempty" valid:"type(string)"` } type Configuration struct { - WebServer *WebServer `yaml:"WebServer,omitempty"` - Mongodb *Mongodb `yaml:"mongodb"` + WebServer *WebServer `yaml:"webServer,omitempty" valid:"optional"` + Mongodb *Mongodb `yaml:"mongodb" valid:"required"` +} + +type Logger struct { + Enable bool `yaml:"enable" valid:"type(bool)"` + Level string `yaml:"level" valid:"required,in(trace|debug|info|warn|error|fatal|panic)"` + ReportCaller bool `yaml:"reportCaller" valid:"type(bool)"` } type WebServer struct { - Scheme string `yaml:"scheme"` - IP string `yaml:"ipv4Address"` - PORT string `yaml:"port"` + Scheme string `yaml:"scheme" valid:"required"` + IP string `yaml:"ipv4Address" valid:"required"` + PORT string `yaml:"port" valid:"required"` } type Mongodb struct { - Name string `yaml:"name"` - Url string `yaml:"url"` + Name string `yaml:"name" valid:"required"` + Url string `yaml:"url" valid:"required"` +} + +func appendInvalid(err error) error { + var errs govalidator.Errors + + if err == nil { + return nil + } + + es := err.(govalidator.Errors).Errors() + for _, e := range es { + errs = append(errs, fmt.Errorf("invalid %w", e)) + } + + return error(errs) +} + +func (c *Config) SetLogEnable(enable bool) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Enable: enable, + Level: "info", + } + } else { + c.Logger.Enable = enable + } +} + +func (c *Config) SetLogLevel(level string) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Level: level, + } + } else { + c.Logger.Level = level + } +} + +func (c *Config) SetLogReportCaller(reportCaller bool) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Level: "info", + ReportCaller: reportCaller, + } + } else { + c.Logger.ReportCaller = reportCaller + } +} + +func (c *Config) GetLogEnable() bool { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return false + } + return c.Logger.Enable +} + +func (c *Config) GetLogLevel() string { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return "info" + } + return c.Logger.Level +} + +func (c *Config) GetLogReportCaller() bool { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return false + } + return c.Logger.ReportCaller } diff --git a/backend/factory/factory.go b/backend/factory/factory.go index 205206fe..57726b37 100644 --- a/backend/factory/factory.go +++ b/backend/factory/factory.go @@ -8,22 +8,45 @@ import ( "fmt" "io/ioutil" + "github.com/asaskevich/govalidator" "gopkg.in/yaml.v2" + + "github.com/free5gc/webconsole/backend/logger" ) -var WebUIConfig Config +var WebuiConfig *Config // TODO: Support configuration update from REST api -func InitConfigFactory(f string) error { +func InitConfigFactory(f string, cfg *Config) error { + if f == "" { + // Use default config path + f = WebuiDefaultConfigPath + } if content, err := ioutil.ReadFile(f); err != nil { - return fmt.Errorf("[Configuration] %+v", err) + return fmt.Errorf("[Factory] %+v", err) } else { - WebUIConfig = Config{} - - if yamlErr := yaml.Unmarshal(content, &WebUIConfig); yamlErr != nil { - return fmt.Errorf("[Configuration] %+v", yamlErr) + logger.CfgLog.Infof("Read config from [%s]", f) + if yamlErr := yaml.Unmarshal(content, &cfg); yamlErr != nil { + return fmt.Errorf("[Factory] %+v", yamlErr) } } return nil } + +func ReadConfig(cfgPath string) (*Config, error) { + cfg := &Config{} + if err := InitConfigFactory(cfgPath, cfg); err != nil { + return nil, fmt.Errorf("ReadConfig [%s] Error: %+v", cfgPath, err) + } + if _, err := cfg.Validate(); err != nil { + validErrs := err.(govalidator.Errors).Errors() + for _, validErr := range validErrs { + logger.CfgLog.Errorf("%+v", validErr) + } + logger.CfgLog.Errorf("[-- PLEASE REFER TO SAMPLE CONFIG FILE COMMENTS --]") + return nil, fmt.Errorf("Config validate Error") + } + + return cfg, nil +} diff --git a/backend/logger/logger.go b/backend/logger/logger.go index db51f6d1..e7b8a089 100644 --- a/backend/logger/logger.go +++ b/backend/logger/logger.go @@ -1,73 +1,33 @@ package logger import ( - "os" - "time" - - formatter "github.com/antonfisher/nested-logrus-formatter" "github.com/sirupsen/logrus" logger_util "github.com/free5gc/util/logger" ) var ( - log *logrus.Logger - AppLog *logrus.Entry - InitLog *logrus.Entry - WebUILog *logrus.Entry - ContextLog *logrus.Entry - GinLog *logrus.Entry + Log *logrus.Logger + NfLog *logrus.Entry + MainLog *logrus.Entry + InitLog *logrus.Entry + ProcLog *logrus.Entry + CtxLog *logrus.Entry + CfgLog *logrus.Entry + GinLog *logrus.Entry ) func init() { - log = logrus.New() - log.SetReportCaller(false) - - log.Formatter = &formatter.Formatter{ - TimestampFormat: time.RFC3339Nano, - TrimMessages: true, - NoFieldsSpace: true, - HideKeys: true, - FieldsOrder: []string{"component", "category"}, + fieldsOrder := []string{ + logger_util.FieldNF, + logger_util.FieldCategory, } - - AppLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "App"}) - InitLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "Init"}) - WebUILog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "WebUI"}) - ContextLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "Context"}) - GinLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "GIN"}) -} - -func LogFileHook(logNfPath string, log5gcPath string) error { - if fullPath, err := logger_util.CreateFree5gcLogFile(log5gcPath); err == nil { - if fullPath != "" { - free5gcLogHook, hookErr := logger_util.NewFileHook(fullPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if hookErr != nil { - return hookErr - } - log.Hooks.Add(free5gcLogHook) - } - } else { - return err - } - - if fullPath, err := logger_util.CreateNfLogFile(logNfPath, "webconsole.log"); err == nil { - selfLogHook, hookErr := logger_util.NewFileHook(fullPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if hookErr != nil { - return hookErr - } - log.Hooks.Add(selfLogHook) - } else { - return err - } - - return nil -} - -func SetLogLevel(level logrus.Level) { - log.SetLevel(level) -} - -func SetReportCaller(enable bool) { - log.SetReportCaller(enable) + Log = logger_util.New(fieldsOrder) + NfLog = Log.WithField(logger_util.FieldNF, "WEBUI") + MainLog = NfLog.WithField(logger_util.FieldCategory, "Main") + InitLog = NfLog.WithField(logger_util.FieldCategory, "Init") + ProcLog = NfLog.WithField(logger_util.FieldCategory, "Proc") + CtxLog = NfLog.WithField(logger_util.FieldCategory, "CTX") + CfgLog = NfLog.WithField(logger_util.FieldCategory, "CFG") + GinLog = NfLog.WithField(logger_util.FieldCategory, "GIN") } diff --git a/backend/webui_context/context.go b/backend/webui_context/context.go index 00036ac7..37aa6346 100644 --- a/backend/webui_context/context.go +++ b/backend/webui_context/context.go @@ -9,7 +9,7 @@ import ( "github.com/free5gc/webconsole/backend/logger" ) -var webuiContext = WEBUIContext{} +var webuiContext WEBUIContext type WEBUIContext struct { NFProfiles []models.NfProfile @@ -22,18 +22,15 @@ type NfOamInstance struct { Uri string } -func init() { -} - func (context *WEBUIContext) UpdateNfProfiles() { nfProfilesRaw, err := mongoapi.RestfulAPIGetMany("NfProfile", nil) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } var nfProfiles []models.NfProfile if err := timedecode.Decode(nfProfilesRaw, &nfProfiles); err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } @@ -105,7 +102,7 @@ func (context *WEBUIContext) GetOamUris(targetNfType models.NfType) (uris []stri return } -func WEBUI_Self() *WEBUIContext { +func GetSelf() *WEBUIContext { return &webuiContext } diff --git a/backend/webui_service/webui_init.go b/backend/webui_service/webui_init.go index a69527e9..ce498ed6 100644 --- a/backend/webui_service/webui_init.go +++ b/backend/webui_service/webui_init.go @@ -1,16 +1,11 @@ package webui_service import ( - "bufio" - "fmt" - "os/exec" - "path/filepath" - "runtime/debug" - "sync" + "io/ioutil" + "os" "github.com/gin-contrib/cors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" "github.com/free5gc/util/mongoapi" "github.com/free5gc/webconsole/backend/WebUI" @@ -19,118 +14,70 @@ import ( "github.com/free5gc/webconsole/backend/webui_context" ) -type WEBUI struct{} - -type ( - // Commands information. - Commands struct { - config string - public string - } -) - -var commands Commands - -var cliCmd = []cli.Flag{ - cli.StringFlag{ - Name: "public, p", - Usage: "Load public path from `FOLDER`", - }, - cli.StringFlag{ - Name: "config, c", - Usage: "Load configuration from `FILE`", - }, - cli.StringFlag{ - Name: "log, l", - Usage: "Output NF log to `FILE`", - }, - cli.StringFlag{ - Name: "log5gc, lc", - Usage: "Output free5gc log to `FILE`", - }, +type WebuiApp struct { + cfg *factory.Config + webuiCtx *webui_context.WEBUIContext } -var initLog *logrus.Entry +func NewApp(cfg *factory.Config) (*WebuiApp, error) { + webui := &WebuiApp{cfg: cfg} + webui.SetLogEnable(cfg.GetLogEnable()) + webui.SetLogLevel(cfg.GetLogLevel()) + webui.SetReportCaller(cfg.GetLogReportCaller()) -func (*WEBUI) GetCliCmd() (flags []cli.Flag) { - return cliCmd + webui.webuiCtx = webui_context.GetSelf() + return webui, nil } -func (webui *WEBUI) Initialize(c *cli.Context) error { - commands = Commands{ - config: c.String("config"), - public: c.String("public"), +func (a *WebuiApp) SetLogEnable(enable bool) { + logger.MainLog.Infof("Log enable is set to [%v]", enable) + if enable && logger.Log.Out == os.Stderr { + return + } else if !enable && logger.Log.Out == ioutil.Discard { + return } - - initLog = logger.InitLog - - if commands.config != "" { - if err := factory.InitConfigFactory(commands.config); err != nil { - return err - } + a.cfg.SetLogEnable(enable) + if enable { + logger.Log.SetOutput(os.Stderr) } else { - if err := factory.InitConfigFactory("./config/webuicfg.yaml"); err != nil { - return err - } - } - - if commands.public != "" { - PublicPath = filepath.Clean(commands.public) + logger.Log.SetOutput(ioutil.Discard) } - - webui.setLogLevel() - - return nil } -func (webui *WEBUI) setLogLevel() { - if factory.WebUIConfig.Logger == nil { - initLog.Warnln("Webconsole config without log level setting!!!") +func (a *WebuiApp) SetLogLevel(level string) { + lvl, err := logrus.ParseLevel(level) + if err != nil { + logger.MainLog.Warnf("Log level [%s] is invalid", level) return } - - if factory.WebUIConfig.Logger.WEBUI != nil { - if factory.WebUIConfig.Logger.WEBUI.DebugLevel != "" { - if level, err := logrus.ParseLevel(factory.WebUIConfig.Logger.WEBUI.DebugLevel); err != nil { - initLog.Warnf("WebUI Log level [%s] is invalid, set to [info] level", - factory.WebUIConfig.Logger.WEBUI.DebugLevel) - logger.SetLogLevel(logrus.InfoLevel) - } else { - initLog.Infof("WebUI Log level is set to [%s] level", level) - logger.SetLogLevel(level) - } - } else { - initLog.Warnln("WebUI Log level not set. Default set to [info] level") - logger.SetLogLevel(logrus.InfoLevel) - } - logger.SetReportCaller(factory.WebUIConfig.Logger.WEBUI.ReportCaller) + logger.MainLog.Infof("Log level is set to [%s]", level) + if lvl == logger.Log.GetLevel() { + return } + a.cfg.SetLogLevel(level) + logger.Log.SetLevel(lvl) } -func (webui *WEBUI) FilterCli(c *cli.Context) (args []string) { - for _, flag := range webui.GetCliCmd() { - name := flag.GetName() - value := fmt.Sprint(c.Generic(name)) - if value == "" { - continue - } - - args = append(args, "--"+name, value) +func (a *WebuiApp) SetReportCaller(reportCaller bool) { + logger.MainLog.Infof("Report Caller is set to [%v]", reportCaller) + if reportCaller == logger.Log.ReportCaller { + return } - return args + a.cfg.SetLogReportCaller(reportCaller) + logger.Log.SetReportCaller(reportCaller) } -func (webui *WEBUI) Start() { +func (a *WebuiApp) Start(tlsKeyLogPath string) { // get config file info from WebUIConfig - mongodb := factory.WebUIConfig.Configuration.Mongodb + mongodb := factory.WebuiConfig.Configuration.Mongodb // Connect to MongoDB if err := mongoapi.SetMongoDB(mongodb.Name, mongodb.Url); err != nil { - initLog.Errorf("Server start err: %+v", err) + logger.InitLog.Errorf("Server start err: %+v", err) return } - initLog.Infoln("Server started") + logger.InitLog.Infoln("Server started") router := WebUI.NewRouter() @@ -146,79 +93,10 @@ func (webui *WEBUI) Start() { MaxAge: 86400, })) - self := webui_context.WEBUI_Self() + self := webui_context.GetSelf() self.UpdateNfProfiles() router.NoRoute(ReturnPublic()) - initLog.Infoln(router.Run(":5000")) -} - -func (webui *WEBUI) Exec(c *cli.Context) error { - initLog.Traceln("args:", c.String("webuicfg")) - args := webui.FilterCli(c) - initLog.Traceln("filter: ", args) - command := exec.Command("./webui", args...) - - if err := webui.Initialize(c); err != nil { - return err - } - - stdout, err := command.StdoutPipe() - if err != nil { - initLog.Fatalln(err) - } - wg := sync.WaitGroup{} - wg.Add(3) - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - in := bufio.NewScanner(stdout) - for in.Scan() { - fmt.Println(in.Text()) - } - wg.Done() - }() - - stderr, err := command.StderrPipe() - if err != nil { - initLog.Fatalln(err) - } - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - in := bufio.NewScanner(stderr) - for in.Scan() { - fmt.Println(in.Text()) - } - wg.Done() - }() - - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - if errCmd := command.Start(); errCmd != nil { - fmt.Println("command.Start Fails!") - } - wg.Done() - }() - - wg.Wait() - - return err + logger.InitLog.Infoln(router.Run(":5000")) } diff --git a/config/webuicfg.yaml b/config/webuicfg.yaml index f837ab76..ed7aff34 100644 --- a/config/webuicfg.yaml +++ b/config/webuicfg.yaml @@ -1,5 +1,5 @@ info: - version: 1.0.0 + version: 1.0.1 description: WebUI initial local configuration configuration: @@ -7,10 +7,7 @@ configuration: name: free5gc # name of the mongodb url: mongodb://localhost:27017 # a valid URL of the mongodb -# the kind of log output -# debugLevel: how detailed to output, value: trace, debug, info, warn, error, fatal, panic -# ReportCaller: enable the caller report or not, value: true or false -logger: - WEBUI: - debugLevel: info - ReportCaller: false +logger: # log output setting + enable: true # true or false + level: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic + reportCaller: false # enable the caller report or not, value: true or false diff --git a/frontend/package.json b/frontend/package.json index 451b419e..37f556ae 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,6 +4,7 @@ "private": true, "homepage": "", "dependencies": { + "@fortawesome/fontawesome-free": "^5.15.2", "autoprefixer": "7.1.2", "axios": "latest", "babel-core": "6.26.3", @@ -12,6 +13,7 @@ "babel-loader": "7.1.1", "babel-preset-react-app": "^3.0.2", "babel-runtime": "6.26.0", + "bootstrap": "~3.3.6", "case-sensitive-paths-webpack-plugin": "2.4.0", "chalk": "1.1.3", "classnames": "^2.2.5", @@ -60,7 +62,7 @@ "whatwg-fetch": "2.0.3" }, "devDependencies": { - "sass": "^1.50.0", + "sass": "1.51.0", "sass-loader": "^7.0.1", "webpack-dev-server": "2.9.7", "ws": "3.3.2" diff --git a/frontend/public/index.html b/frontend/public/index.html index d038b4d1..3182307d 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -4,10 +4,6 @@ - - - -