package main import ( "context" "encoding/hex" "errors" "fmt" "math/rand" "net/http" "sparrow/pkg/models" "sparrow/pkg/rpcs" "sparrow/pkg/server" "sparrow/pkg/token" "strconv" "strings" "github.com/martini-contrib/render" ) const ( ErrOK = 0 ErrSystemFault = 10001 ErrDeviceNotFound = 10002 ErrWrongSecret = 10003 ErrProtocolNotSuported = 10004 ) func renderError(code int, err error) Common { result := Common{} result.Code = code result.Message = err.Error() server.Log.Error(err.Error()) return result } // DeviceRegisterArgs device register args type DeviceRegisterArgs struct { ProductKey string `json:"product_key" binding:"required"` DeviceCode string `json:"device_code" binding:"required"` Version string `json:"version" binding:"required"` ModuleName string `json:"module" binding:"required"` } // DeviceAuthArgs device authentication args type DeviceAuthArgs struct { DeviceId int64 `json:"device_id" binding:"required"` DeviceSecret string `json:"device_secret" binding:"required"` Protocol string `json:"protocol" binding:"required"` } type AccessAuthArgs struct { UserName string `json:"username"` Password string `json:"password"` } type AccessAuthResp struct { Result string `json:"result"` // 可选 "allow" | "deny" | "ignore" IsSuperuser bool `json:"is_superuser"` // false } // RegisterDevice 设备激活 func RegisterDevice(args DeviceRegisterArgs, r render.Render) { server.Log.Printf("ACTION RegisterDevice, args:: %v ", args) rpcargs := &rpcs.ArgsDeviceRegister{ ProductKey: args.ProductKey, DeviceCode: args.DeviceCode, DeviceVersion: args.Version, ModuleName: args.ModuleName, } device := &models.Device{} err := server.RPCCallByName(nil, rpcs.RegistryServerName, "Registry.RegisterDevice", rpcargs, device) if err != nil { r.JSON(http.StatusOK, renderError(ErrSystemFault, err)) return } server.Log.Infof("register device success: %v", device) result := DeviceRegisterResponse{} result.Data = DeviceRegisterData{ DeviceId: int64(device.ID), DeviceSecret: device.DeviceSecret, DeviceKey: device.DeviceKey, DeviceIdentifier: device.DeviceIdentifier, } r.JSON(http.StatusOK, result) return } // DeviceAccessAuth emqx 设备接入认证 func DeviceAccessAuth(args AccessAuthArgs, r render.Render) { server.Log.Printf("ACTION DeviceAccessAuth, args:: %v", args) result := AccessAuthResp{} if (args.UserName == "sparrow_test" && args.Password == "sparrow_test") || args.Password == "z6Kq9F3LbT" { result.Result = "allow" r.JSON(http.StatusOK, result) return } deviceId, err := ClientIDToDeviceID(args.UserName) if err != nil { server.Log.Errorf("invalid Identify: %s", args.UserName) result.Result = "deny" r.JSON(http.StatusOK, result) return } device := &models.Device{} err = server.RPCCallByName(nil, rpcs.RegistryServerName, "Registry.FindDeviceById", deviceId, device) if err != nil { server.Log.Errorf("device not found %d", deviceId) result.Result = "deny" r.JSON(http.StatusOK, result) return } // parse token token, err := hex.DecodeString(args.Password) if err != nil { server.Log.Errorf("token format error : %v", err) result.Result = "deny" r.JSON(http.StatusOK, result) return } // validate token if err := validateToken(device.RecordId, token); err != nil { server.Log.Errorf("validate token error : %v", err) result.Result = "deny" r.JSON(http.StatusOK, result) return } result.Result = "allow" r.JSON(http.StatusOK, result) } func validateToken(deviceRecordId string, token []byte) error { args := rpcs.ArgsValidateDeviceAccessToken{ Id: deviceRecordId, AccessToken: token, } reply := rpcs.ReplyValidateDeviceAccessToken{} err := server.RPCCallByName(nil, rpcs.DeviceManagerName, "DeviceManager.ValidateDeviceAccessToken", args, &reply) if err != nil { return err } return nil } // AuthDevice device auth func AuthDevice(args DeviceAuthArgs, r render.Render) { device := &models.Device{} arg := uint64(args.DeviceId) err := server.RPCCallByName(context.Background(), rpcs.RegistryServerName, "Registry.FindDeviceById", &arg, device) if err != nil { r.JSON(http.StatusOK, renderError(ErrDeviceNotFound, err)) return } fmt.Printf(fmt.Sprintf("%s, %s", device.DeviceSecret, args.DeviceSecret)) if device.DeviceSecret != args.DeviceSecret { // device secret is wrong. r.JSON(http.StatusOK, renderError(ErrWrongSecret, errors.New("wrong device secret."))) return } hepler := token.NewHelper(*confRedisHost, *confRedisPort, *confRedisDb) token, err := hepler.GenerateToken(device.RecordId) if err != nil { r.JSON(http.StatusOK, renderError(ErrSystemFault, err)) return } var hosts []string switch strings.ToLower(args.Protocol) { case "http": hosts, err = server.GetServerHosts(strings.ToUpper(args.Protocol)+"Access", "httphost") case "mqtt": hosts, err = server.GetServerHosts(strings.ToUpper(args.Protocol)+"Access", "tcphost") case "coap": hosts, err = server.GetServerHosts(strings.ToUpper(args.Protocol)+"Access", "udphost") case "mqttx": // TODO: 增加配置文件支持 hosts = []string{ "114.115.251.196:1883", } default: err = errors.New("unsuported protocol: " + args.Protocol) } if err != nil { r.JSON(http.StatusOK, renderError(ErrProtocolNotSuported, err)) return } // just get a random host host := hosts[rand.Intn(len(hosts))] result := DeviceAuthResponse{} result.Data = DeviceAuthData{ AccessToken: hex.EncodeToString(token), AccessAddr: host, } server.Log.Infof("auth device success: %v, token: %x, access: %v", device, token, host) r.JSON(http.StatusOK, result) return } func ClientIDToDeviceID(identify string) (uint64, error) { deviceId, err := strconv.ParseUint(identify, 16, 64) if err != nil { return uint64(0), err } return deviceId, nil }