func (s *networkServerRPC) ValidateContext(ctx context.Context) error { md, ok := metadata.FromContext(ctx) if !ok { return errors.NewErrInternal("Could not get metadata from context") } var id, token string if ids, ok := md["id"]; ok && len(ids) == 1 { id = ids[0] } if id == "" { return errors.NewErrInvalidArgument("Metadata", "id missing") } if tokens, ok := md["token"]; ok && len(tokens) == 1 { token = tokens[0] } if token == "" { return errors.NewErrInvalidArgument("Metadata", "token missing") } var claims *jwt.StandardClaims claims, err := security.ValidateJWT(token, []byte(s.networkServer.(*networkServer).Identity.PublicKey)) if err != nil { return err } if claims.Subject != id { return errors.NewErrInvalidArgument("Metadata", "token was issued for a different component id") } return nil }
// Decode decodes the payload using the Decoder function into a map func (f *UplinkFunctions) Decode(payload []byte, port uint8) (map[string]interface{}, error) { if f.Decoder == "" { return nil, errors.NewErrInternal("Decoder function not set") } env := map[string]interface{}{ "payload": payload, "port": port, } code := fmt.Sprintf(` %s; Decoder(payload.slice(0), port); `, f.Decoder) value, err := functions.RunCode("decoder", code, env, timeOut, f.Logger) if err != nil { return nil, err } if !value.IsObject() { return nil, errors.NewErrInvalidArgument("Decoder", "does not return an object") } v, _ := value.Export() m, ok := v.(map[string]interface{}) if !ok { return nil, errors.NewErrInvalidArgument("Decoder", "does not return an object") } return m, nil }
func (r *routerRPC) gatewayFromMetadata(md metadata.MD) (gtw *gateway.Gateway, err error) { gatewayID, err := api.IDFromMetadata(md) if err != nil { return nil, err } token, _ := api.TokenFromMetadata(md) if !viper.GetBool("router.skip-verify-gateway-token") { if token == "" { return nil, errors.NewErrPermissionDenied("No gateway token supplied") } if r.router.TokenKeyProvider == nil { return nil, errors.NewErrInternal("No token provider configured") } claims, err := claims.FromToken(r.router.TokenKeyProvider, token) if err != nil { return nil, errors.NewErrPermissionDenied(fmt.Sprintf("Gateway token invalid: %s", err.Error())) } if claims.Type != "gateway" || claims.Subject != gatewayID { return nil, errors.NewErrPermissionDenied("Gateway token not consistent") } } gtw = r.router.getGateway(gatewayID) gtw.SetToken(token) return gtw, nil }
func (b *broker) getRouter(id string) (chan<- *pb.DownlinkMessage, error) { b.routersLock.RLock() defer b.routersLock.RUnlock() if router, ok := b.routers[id]; ok { return router, nil } return nil, errors.NewErrInternal(fmt.Sprintf("Router %s not active", id)) }
// ValidateNetworkContext validates the context of a network request (router-broker, broker-handler, etc) func (c *Component) ValidateNetworkContext(ctx context.Context) (component *pb_discovery.Announcement, err error) { defer func() { if err != nil { time.Sleep(time.Second) } }() md, ok := metadata.FromContext(ctx) if !ok { err = errors.NewErrInternal("Could not get metadata from context") return } var id, serviceName, token string if ids, ok := md["id"]; ok && len(ids) == 1 { id = ids[0] } if id == "" { err = errors.NewErrInvalidArgument("Metadata", "id missing") return } if serviceNames, ok := md["service-name"]; ok && len(serviceNames) == 1 { serviceName = serviceNames[0] } if serviceName == "" { err = errors.NewErrInvalidArgument("Metadata", "service-name missing") return } if tokens, ok := md["token"]; ok && len(tokens) == 1 { token = tokens[0] } var announcement *pb_discovery.Announcement announcement, err = c.Discover(serviceName, id) if err != nil { return } if announcement.PublicKey == "" { return announcement, nil } if token == "" { err = errors.NewErrInvalidArgument("Metadata", "token missing") return } var claims *jwt.StandardClaims claims, err = security.ValidateJWT(token, []byte(announcement.PublicKey)) if err != nil { return } if claims.Issuer != id { err = errors.NewErrInvalidArgument("Metadata", "token was issued by different component id") return } return announcement, nil }
func (b *broker) ActivateRouter(id string) (<-chan *pb.DownlinkMessage, error) { b.routersLock.Lock() defer b.routersLock.Unlock() if existing, ok := b.routers[id]; ok { return existing, errors.NewErrInternal(fmt.Sprintf("Router %s already active", id)) } b.routers[id] = make(chan *pb.DownlinkMessage) return b.routers[id], nil }
func (b *broker) getHandlerUplink(id string) (chan<- *pb.DeduplicatedUplinkMessage, error) { hdl := b.getHandler(id) hdl.Lock() defer hdl.Unlock() if hdl.uplink == nil { return nil, errors.NewErrInternal(fmt.Sprintf("Handler %s not active", id)) } return hdl.uplink, nil }
func (b *broker) ActivateHandlerUplink(id string) (<-chan *pb.DeduplicatedUplinkMessage, error) { hdl := b.getHandler(id) hdl.Lock() defer hdl.Unlock() if hdl.uplink != nil { return hdl.uplink, errors.NewErrInternal(fmt.Sprintf("Handler %s already active", id)) } hdl.uplink = make(chan *pb.DeduplicatedUplinkMessage) return hdl.uplink, nil }
func (b *broker) DeactivateRouter(id string) error { b.routersLock.Lock() defer b.routersLock.Unlock() if channel, ok := b.routers[id]; ok { close(channel) delete(b.routers, id) return nil } return errors.NewErrInternal(fmt.Sprintf("Router %s not active", id)) }
// Get the frequency plan for the given region func Get(region string) (frequencyPlan FrequencyPlan, err error) { switch region { case pb_lorawan.Region_EU_863_870.String(): frequencyPlan.Band, err = lora.GetConfig(lora.EU_863_870, false, lorawan.DwellTimeNoLimit) // TTN uses SF9BW125 in RX2 frequencyPlan.RX2DataRate = 3 // TTN frequency plan includes extra channels next to the default channels: frequencyPlan.UplinkChannels = []lora.Channel{ lora.Channel{Frequency: 868100000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 868300000, DataRates: []int{0, 1, 2, 3, 4, 5, 6}}, // Also SF7BW250 lora.Channel{Frequency: 868500000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 868800000, DataRates: []int{7}}, // FSK 50kbps lora.Channel{Frequency: 867100000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867300000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867500000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867700000, DataRates: []int{0, 1, 2, 3, 4, 5}}, lora.Channel{Frequency: 867900000, DataRates: []int{0, 1, 2, 3, 4, 5}}, } frequencyPlan.DownlinkChannels = frequencyPlan.UplinkChannels frequencyPlan.CFList = &lorawan.CFList{867100000, 867300000, 867500000, 867700000, 867900000} case pb_lorawan.Region_US_902_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.US_902_928, false, lorawan.DwellTime400ms) case pb_lorawan.Region_CN_779_787.String(): err = errors.NewErrInternal("China 779-787 MHz band not supported") case pb_lorawan.Region_EU_433.String(): err = errors.NewErrInternal("Europe 433 MHz band not supported") case pb_lorawan.Region_AU_915_928.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AU_915_928, false, lorawan.DwellTime400ms) case pb_lorawan.Region_CN_470_510.String(): frequencyPlan.Band, err = lora.GetConfig(lora.CN_470_510, false, lorawan.DwellTimeNoLimit) case pb_lorawan.Region_AS_923.String(): frequencyPlan.Band, err = lora.GetConfig(lora.AS_923, false, lorawan.DwellTime400ms) case pb_lorawan.Region_KR_920_923.String(): frequencyPlan.Band, err = lora.GetConfig(lora.KR_920_923, false, lorawan.DwellTimeNoLimit) default: err = errors.NewErrInvalidArgument("Frequency Band", "unknown") } if err != nil { return } return }
func (b *broker) DeactivateHandlerUplink(id string) error { hdl := b.getHandler(id) hdl.Lock() defer hdl.Unlock() if hdl.uplink == nil { return errors.NewErrInternal(fmt.Sprintf("Handler %s not active", id)) } close(hdl.uplink) hdl.uplink = nil return nil }
func RunCode(name, code string, env map[string]interface{}, timeout time.Duration, logger Logger) (otto.Value, error) { vm := otto.New() // load the environment for key, val := range env { vm.Set(key, val) } if logger == nil { logger = Ignore } logger.Enter(name) vm.Set("__log", func(call otto.FunctionCall) otto.Value { logger.Log(call) return otto.UndefinedValue() }) vm.Run("console.log = __log") var value otto.Value var err error start := time.Now() defer func() { duration := time.Since(start) if caught := recover(); caught != nil { if caught == errTimeOutExceeded { value = otto.Value{} err = errors.NewErrInternal(fmt.Sprintf("Interrupted javascript execution after %v", duration)) return } // if this is not the our timeout interrupt, raise the panic again // so someone else can handle it panic(caught) } }() vm.Interrupt = make(chan func(), 1) go func() { time.Sleep(timeout) vm.Interrupt <- func() { panic(errTimeOutExceeded) } }() val, err := vm.Run(code) return val, err }
// UpdateTokenKey updates the OAuth Bearer token key func (c *Component) UpdateTokenKey() error { if c.TokenKeyProvider == nil { return errors.NewErrInternal("No public key provider configured for token validation") } // Set up Auth Server Token Validation err := c.TokenKeyProvider.Update() if err != nil { c.Ctx.Warnf("ttn: Failed to refresh public keys for token validation: %s", err.Error()) } else { c.Ctx.Info("ttn: Got public keys for token validation") } return nil }
// ValidateTTNAuthContext gets a token from the context and validates it func (c *Component) ValidateTTNAuthContext(ctx context.Context) (*claims.Claims, error) { token, err := api.TokenFromContext(ctx) if err != nil { return nil, err } if c.TokenKeyProvider == nil { return nil, errors.NewErrInternal("No token provider configured") } claims, err := claims.FromToken(c.TokenKeyProvider, token) if err != nil { return nil, errors.NewErrPermissionDenied(err.Error()) } return claims, nil }
func (b *brokerManager) RegisterApplicationHandler(ctx context.Context, in *pb.ApplicationHandlerRegistration) (*empty.Empty, error) { claims, err := b.broker.Component.ValidateTTNAuthContext(ctx) if err != nil { return nil, err } if err := in.Validate(); err != nil { return nil, errors.Wrap(err, "Invalid Application Handler Registration") } if !claims.AppRight(in.AppId, rights.AppSettings) { return nil, errors.NewErrPermissionDenied("No access to this application") } // Add Handler in local cache handler, err := b.broker.Discovery.Get("handler", in.HandlerId) if err != nil { return nil, errors.NewErrInternal("Could not get Handler Announcement") } handler.Metadata = append(handler.Metadata, &discovery.Metadata{Metadata: &discovery.Metadata_AppId{ AppId: in.AppId, }}) return &empty.Empty{}, nil }
func (r *router) SubscribeDownlink(gatewayID string, subscriptionID string) (<-chan *pb.DownlinkMessage, error) { ctx := r.Ctx.WithFields(log.Fields{ "GatewayID": gatewayID, }) gateway := r.getGateway(gatewayID) if fromSchedule := gateway.Schedule.Subscribe(subscriptionID); fromSchedule != nil { toGateway := make(chan *pb.DownlinkMessage) go func() { ctx.Debug("Activate downlink") for message := range fromSchedule { gateway.Utilization.AddTx(message) ctx.Debug("Send downlink") toGateway <- message } ctx.Debug("Deactivate downlink") close(toGateway) }() return toGateway, nil } return nil, errors.NewErrInternal(fmt.Sprintf("Already subscribed to downlink for %s", gatewayID)) }
func (b *broker) HandleUplink(uplink *pb.UplinkMessage) (err error) { ctx := b.Ctx.WithField("GatewayID", uplink.GatewayMetadata.GatewayId) start := time.Now() defer func() { if err != nil { ctx.WithError(err).Warn("Could not handle uplink") } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled uplink") } }() time := time.Now() b.status.uplink.Mark(1) // De-duplicate uplink messages duplicates := b.deduplicateUplink(uplink) if len(duplicates) == 0 { return nil } b.status.uplinkUnique.Mark(1) ctx = ctx.WithField("Duplicates", len(duplicates)) base := duplicates[0] if base.ProtocolMetadata.GetLorawan() == nil { return errors.NewErrInvalidArgument("Uplink", "does not contain LoRaWAN metadata") } // LoRaWAN: Unmarshal var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(base.Payload) if err != nil { return err } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Uplink", "does not contain a MAC payload") } // Request devices from NS devAddr := types.DevAddr(macPayload.FHDR.DevAddr) ctx = ctx.WithFields(log.Fields{ "DevAddr": devAddr, "FCnt": macPayload.FHDR.FCnt, }) var getDevicesResp *networkserver.DevicesResponse getDevicesResp, err = b.ns.GetDevices(b.Component.GetContext(b.nsToken), &networkserver.DevicesRequest{ DevAddr: &devAddr, FCnt: macPayload.FHDR.FCnt, }) if err != nil { return errors.Wrap(errors.FromGRPCError(err), "NetworkServer did not return devices") } b.status.deduplication.Update(int64(len(getDevicesResp.Results))) if len(getDevicesResp.Results) == 0 { return errors.NewErrNotFound(fmt.Sprintf("Device with DevAddr %s and FCnt <= %d", devAddr, macPayload.FHDR.FCnt)) } ctx = ctx.WithField("DevAddrResults", len(getDevicesResp.Results)) // Sort by FCntUp to optimize the number of MIC checks sort.Sort(ByFCntUp(getDevicesResp.Results)) // Find AppEUI/DevEUI through MIC check var device *pb_lorawan.Device var micChecks int var originalFCnt uint32 for _, candidate := range getDevicesResp.Results { nwkSKey := lorawan.AES128Key(*candidate.NwkSKey) // First check with the 16 bit counter micChecks++ ok, err = phyPayload.ValidateMIC(nwkSKey) if err != nil { return err } if ok { device = candidate break } originalFCnt = macPayload.FHDR.FCnt if candidate.Uses32BitFCnt { macPayload.FHDR.FCnt = fcnt.GetFull(candidate.FCntUp, uint16(originalFCnt)) // If 32 bit counter has different value, perform another MIC check if macPayload.FHDR.FCnt != originalFCnt { micChecks++ ok, err = phyPayload.ValidateMIC(nwkSKey) if err != nil { return err } if ok { device = candidate break } } } return errors.NewErrNotFound("device that validates MIC") } ctx = ctx.WithFields(log.Fields{ "MICChecks": micChecks, "DevEUI": device.DevEui, "AppEUI": device.AppEui, "AppID": device.AppId, "DevID": device.DevId, "FCnt": originalFCnt, }) if macPayload.FHDR.FCnt != originalFCnt { ctx = ctx.WithField("RealFCnt", macPayload.FHDR.FCnt) } if device.DisableFCntCheck { // TODO: Add warning to message? } else if device.FCntUp == 0 { } else if macPayload.FHDR.FCnt <= device.FCntUp || macPayload.FHDR.FCnt-device.FCntUp > maxFCntGap { // Replay attack or FCnt gap too big return errors.NewErrNotFound("device with matching FCnt") } // Add FCnt to Metadata (because it's not marshaled in lorawan payload) base.ProtocolMetadata.GetLorawan().FCnt = macPayload.FHDR.FCnt // Collect GatewayMetadata and DownlinkOptions var gatewayMetadata []*gateway.RxMetadata var downlinkOptions []*pb.DownlinkOption var downlinkMessage *pb.DownlinkMessage for _, duplicate := range duplicates { gatewayMetadata = append(gatewayMetadata, duplicate.GatewayMetadata) downlinkOptions = append(downlinkOptions, duplicate.DownlinkOptions...) } // Select best DownlinkOption if len(downlinkOptions) > 0 { downlinkMessage = &pb.DownlinkMessage{ DevEui: device.DevEui, AppEui: device.AppEui, AppId: device.AppId, DevId: device.DevId, DownlinkOption: selectBestDownlink(downlinkOptions), } } // Build Uplink deduplicatedUplink := &pb.DeduplicatedUplinkMessage{ Payload: base.Payload, DevEui: device.DevEui, DevId: device.DevId, AppEui: device.AppEui, AppId: device.AppId, ProtocolMetadata: base.ProtocolMetadata, GatewayMetadata: gatewayMetadata, ServerTime: time.UnixNano(), ResponseTemplate: downlinkMessage, } // Pass Uplink through NS deduplicatedUplink, err = b.ns.Uplink(b.Component.GetContext(b.nsToken), deduplicatedUplink) if err != nil { return errors.Wrap(errors.FromGRPCError(err), "NetworkServer did not handle uplink") } var announcements []*pb_discovery.Announcement announcements, err = b.Discovery.GetAllHandlersForAppID(device.AppId) if err != nil { return err } if len(announcements) == 0 { return errors.NewErrNotFound(fmt.Sprintf("Handler for AppID %s", device.AppId)) } if len(announcements) > 1 { return errors.NewErrInternal(fmt.Sprintf("Multiple Handlers for AppID %s", device.AppId)) } var handler chan<- *pb.DeduplicatedUplinkMessage handler, err = b.getHandlerUplink(announcements[0].Id) if err != nil { return err } handler <- deduplicatedUplink return nil }
package api import ( "github.com/TheThingsNetwork/ttn/utils/errors" "golang.org/x/net/context" // See https://github.com/grpc/grpc-go/issues/711" "google.golang.org/grpc/metadata" ) // Errors that are returned when an item could not be retrieved var ( ErrContext = errors.NewErrInternal("Could not get metadata from context") ErrNoToken = errors.NewErrInvalidArgument("Metadata", "token missing") ErrNoKey = errors.NewErrInvalidArgument("Metadata", "key missing") ErrNoID = errors.NewErrInvalidArgument("Metadata", "id missing") ) func MetadataFromContext(ctx context.Context) (metadata.MD, error) { md, ok := metadata.FromContext(ctx) if !ok { return md, ErrContext } return md, nil } func IDFromMetadata(md metadata.MD) (string, error) { id, ok := md["id"] if !ok || len(id) == 0 { return "", ErrNoID } return id[0], nil }
func (h *handler) ConvertFromLoRaWAN(ctx log.Interface, ttnUp *pb_broker.DeduplicatedUplinkMessage, appUp *types.UplinkMessage) error { // Find Device dev, err := h.devices.Get(ttnUp.AppId, ttnUp.DevId) if err != nil { return err } // Check for LoRaWAN if lorawan := ttnUp.ProtocolMetadata.GetLorawan(); lorawan == nil { return errors.NewErrInvalidArgument("Activation", "does not contain LoRaWAN metadata") } // LoRaWAN: Unmarshal Uplink var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(ttnUp.Payload) if err != nil { return err } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Uplink", "does not contain a MAC payload") } macPayload.FHDR.FCnt = ttnUp.ProtocolMetadata.GetLorawan().FCnt appUp.FCnt = macPayload.FHDR.FCnt ctx = ctx.WithField("FCnt", appUp.FCnt) // LoRaWAN: Validate MIC ok, err = phyPayload.ValidateMIC(lorawan.AES128Key(dev.NwkSKey)) if err != nil { return err } if !ok { return errors.NewErrNotFound("device that validates MIC") } // LoRaWAN: Decrypt if macPayload.FPort != nil && *macPayload.FPort != 0 && len(macPayload.FRMPayload) == 1 { appUp.FPort = *macPayload.FPort ctx = ctx.WithField("FCnt", appUp.FPort) if err := phyPayload.DecryptFRMPayload(lorawan.AES128Key(dev.AppSKey)); err != nil { return errors.NewErrInternal("Could not decrypt payload") } if len(macPayload.FRMPayload) == 1 { payload, ok := macPayload.FRMPayload[0].(*lorawan.DataPayload) if !ok { return errors.NewErrInvalidArgument("Uplink FRMPayload", "must be of type *lorawan.DataPayload") } appUp.PayloadRaw = payload.Bytes } } // LoRaWAN: Publish ACKs as events if macPayload.FHDR.FCtrl.ACK { h.mqttEvent <- &types.DeviceEvent{ AppID: appUp.AppID, DevID: appUp.DevID, Event: types.DownlinkAckEvent, } } return nil }
func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivationRequest) (res *pb.DeviceActivationResponse, err error) { ctx := r.Ctx.WithFields(log.Fields{ "GatewayID": gatewayID, "AppEUI": *activation.AppEui, "DevEUI": *activation.DevEui, }) start := time.Now() defer func() { if err != nil { ctx.WithError(err).Warn("Could not handle activation") } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation") } }() r.status.activations.Mark(1) gateway := r.getGateway(gatewayID) gateway.LastSeen = time.Now() uplink := &pb.UplinkMessage{ Payload: activation.Payload, ProtocolMetadata: activation.ProtocolMetadata, GatewayMetadata: activation.GatewayMetadata, } if err = gateway.HandleUplink(uplink); err != nil { return nil, err } if !gateway.Schedule.IsActive() { return nil, errors.NewErrInternal(fmt.Sprintf("Gateway %s not available for downlink", gatewayID)) } downlinkOptions := r.buildDownlinkOptions(uplink, true, gateway) // Find Broker brokers, err := r.Discovery.GetAll("broker") if err != nil { return nil, err } // Prepare request request := &pb_broker.DeviceActivationRequest{ Payload: activation.Payload, DevEui: activation.DevEui, AppEui: activation.AppEui, ProtocolMetadata: activation.ProtocolMetadata, GatewayMetadata: activation.GatewayMetadata, ActivationMetadata: &pb_protocol.ActivationMetadata{ Protocol: &pb_protocol.ActivationMetadata_Lorawan{ Lorawan: &pb_lorawan.ActivationMetadata{ AppEui: activation.AppEui, DevEui: activation.DevEui, }, }, }, DownlinkOptions: downlinkOptions, } // Prepare LoRaWAN activation status, err := gateway.Status.Get() if err != nil { return nil, err } region := status.Region if region == "" { region = band.Guess(uplink.GatewayMetadata.Frequency) } band, err := band.Get(region) if err != nil { return nil, err } lorawan := request.ActivationMetadata.GetLorawan() lorawan.Rx1DrOffset = 0 lorawan.Rx2Dr = uint32(band.RX2DataRate) lorawan.RxDelay = uint32(band.ReceiveDelay1.Seconds()) if band.CFList != nil { lorawan.CfList = new(pb_lorawan.CFList) for _, freq := range band.CFList { lorawan.CfList.Freq = append(lorawan.CfList.Freq, freq) } } ctx = ctx.WithField("NumBrokers", len(brokers)) // Forward to all brokers and collect responses var wg sync.WaitGroup responses := make(chan *pb_broker.DeviceActivationResponse, len(brokers)) for _, broker := range brokers { broker, err := r.getBroker(broker) if err != nil { continue } // Do async request wg.Add(1) go func() { res, err := broker.client.Activate(r.Component.GetContext(""), request) if err == nil && res != nil { responses <- res } wg.Done() }() } // Make sure to close channel when all requests are done go func() { wg.Wait() close(responses) }() var gotFirst bool for res := range responses { if gotFirst { ctx.Warn("Duplicate Activation Response") } else { gotFirst = true downlink := &pb_broker.DownlinkMessage{ Payload: res.Payload, Message: res.Message, DownlinkOption: res.DownlinkOption, } err := r.HandleDownlink(downlink) if err != nil { ctx.Warn("Could not send downlink for Activation") gotFirst = false // try again } } } // Activation not accepted by any broker if !gotFirst { ctx.Debug("Activation not accepted at this gateway") return nil, errors.New("Activation not accepted at this Gateway") } // Activation accepted by (at least one) broker ctx.Debug("Activation accepted") return &pb.DeviceActivationResponse{}, nil }
// Copyright © 2016 The Things Network // Use of this source code is governed by the MIT license that can be found in the LICENSE file. package functions import ( "fmt" "time" "github.com/TheThingsNetwork/ttn/utils/errors" "github.com/robertkrimen/otto" ) var errTimeOutExceeded = errors.NewErrInternal("Code has been running to long") func RunCode(name, code string, env map[string]interface{}, timeout time.Duration, logger Logger) (otto.Value, error) { vm := otto.New() // load the environment for key, val := range env { vm.Set(key, val) } if logger == nil { logger = Ignore } logger.Enter(name) vm.Set("__log", func(call otto.FunctionCall) otto.Value { logger.Log(call) return otto.UndefinedValue()