Beispiel #1
0
// Authenticate pusher
// see: https://gist.github.com/mloughran/376898
//
// The signature is a HMAC SHA256 hex digest.
// This is generated by signing a string made up of the following components concatenated with newline characters \n.
//
//  * The uppercase request method (e.g. POST)
//  * The request path (e.g. /some/resource)
//  * The query parameters sorted by key, with keys converted to lowercase, then joined as in the query string.
//    Note that the string must not be url escaped (e.g. given the keys auth_key: foo, Name: Something else, you get auth_key=foo&name=Something else)
func restAuthenticationHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		appID := vars["app_id"]

		app, err := conf.GetAppByAppID(appID)

		if err != nil {
			log.Error(err)
			http.Error(w, "Not authorized", http.StatusUnauthorized)
			return
		}

		params := r.URL.Query()

		signature := params.Get("auth_signature")
		params.Del("auth_signature")

		queryString := prepareQueryString(params)

		toSign := strings.ToUpper(r.Method) + "\n" + r.URL.Path + "\n" + queryString

		if utils.HashMAC([]byte(toSign), []byte(app.Secret)) == signature {
			h.ServeHTTP(w, r)
		} else {
			log.Error("Not authorized")
			http.Error(w, "Not authorized", http.StatusUnauthorized)
		}
	})
}
Beispiel #2
0
func triggerHook(name string, a *app, c *channel, event hookEvent) {
	if !a.WebHooks {
		log.Infof("Webhooks are not enabled for app: %s", a.Name)
		return
	}

	go func() {
		log.Infof("Triggering %s event", name)

		hook := webHook{TimeMs: time.Now().Unix()}

		hook.Events = append(hook.Events, event)

		var js []byte
		var err error

		js, err = json.Marshal(hook)

		if err != nil {
			log.Errorf("Error decoding json: %+v", err)
			return
		}

		var req *http.Request

		req, err = http.NewRequest("POST", a.URLWebHook, bytes.NewReader(js))
		if err != nil {
			log.Errorf("Error creating request: %+v", err)
			return
		}

		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("X-Pusher-Key", a.Key)
		req.Header.Set("X-Pusher-Signature", utils.HashMAC(js, []byte(a.Secret)))

		log.V(1).Infof("%+v", req.Header)
		log.V(1).Infof("%+v", string(js))

		if _, err := http.DefaultClient.Do(req); err != nil {
			log.Errorf("Error posting %s event: %+v", name, err)
		}
	}()
}
Beispiel #3
0
// Handle messages
//
// If there is an unrecoverable error then break the loop,
// otherwise just keep going.
func onMessage(conn *websocket.Conn, w http.ResponseWriter, r *http.Request, sessionID string, app *app) {
	var event struct {
		Event string `json:"event"`
	}

	for {
		_, message, err := conn.ReadMessage()

		if err != nil {
			log.Errorf("%+v", err)
			switch err {
			case io.EOF:
				onClose(sessionID, app)
			default:
				emitWSError(newGenericReconnectImmediatelyError(), conn)
			}
			break
		}

		if err := json.Unmarshal(message, &event); err != nil {
			emitWSError(newGenericReconnectImmediatelyError(), conn)
			break
		}

		log.Infof("websockets: Handling %s event", event.Event)

		switch event.Event {
		case "pusher:ping":
			if err := conn.WriteJSON(newPongEvent()); err != nil {
				emitWSError(newGenericReconnectImmediatelyError(), conn)
			}
		case "pusher:subscribe":
			subscribeEvent := subscribeEvent{}

			if err := json.Unmarshal(message, &subscribeEvent); err != nil {
				emitWSError(newGenericReconnectImmediatelyError(), conn)
				break
			}

			connection, err := app.FindConnection(sessionID)

			if err != nil {
				emitWSError(newGenericReconnectImmediatelyError(), conn)
				break
			}

			channelName := strings.TrimSpace(subscribeEvent.Data.Channel)

			if !utils.IsChannelNameValid(channelName) {
				emitWSError(newGenericError(fmt.Sprintf("This channel name is not valid")), conn)
				break
			}

			isPresence := strings.HasPrefix(channelName, "presence-")
			isPrivate := strings.HasPrefix(channelName, "private-")

			if isPresence || isPrivate {
				toSign := []string{connection.SocketID, channelName}

				if isPresence {
					toSign = append(toSign, subscribeEvent.Data.ChannelData)
				}

				expectedAuthKey := fmt.Sprintf("%s:%s", app.Key, utils.HashMAC([]byte(strings.Join(toSign, ":")), []byte(app.Secret)))
				if subscribeEvent.Data.Auth != expectedAuthKey {
					emitWSError(newGenericError(fmt.Sprintf("Auth value for subscription to %s is invalid", channelName)), conn)
					continue
				}
			}

			channel := app.FindOrCreateChannelByChannelID(channelName)
			log.Info(subscribeEvent.Data.ChannelData)

			if err := app.Subscribe(channel, connection, subscribeEvent.Data.ChannelData); err != nil {
				emitWSError(newGenericReconnectImmediatelyError(), conn)
			}
		case "pusher:unsubscribe":
			unsubscribeEvent := unsubscribeEvent{}

			if err := json.Unmarshal(message, &unsubscribeEvent); err != nil {
				emitWSError(newGenericReconnectImmediatelyError(), conn)
			}

			connection, err := app.FindConnection(sessionID)

			if err != nil {
				emitWSError(newGenericError(fmt.Sprintf("Could not find a connection with the id %s", sessionID)), conn)
			}

			channel, err := app.FindChannelByChannelID(unsubscribeEvent.Data.Channel)

			if err != nil {
				emitWSError(newGenericError(fmt.Sprintf("Could not find a channel with the id %s", unsubscribeEvent.Data.Channel)), conn)
			}

			if err := app.Unsubscribe(channel, connection); err != nil {
				emitWSError(newGenericReconnectImmediatelyError(), conn)
				break
			}
		default: // CLient Events ??
			// see http://pusher.com/docs/client_api_guide/client_events#trigger-events
			if strings.HasPrefix(event.Event, "client-") {
				if !app.UserEvents {
					emitWSError(newGenericError("To send client events, you must enable this feature in the Settings."), conn)
				}

				clientEvent := rawEvent{}

				if err := json.Unmarshal(message, &clientEvent); err != nil {
					log.Error(err)
					emitWSError(newGenericReconnectImmediatelyError(), conn)
					break
				}

				channel, err := app.FindChannelByChannelID(clientEvent.Channel)

				if err != nil {
					emitWSError(newGenericError(fmt.Sprintf("Could not find a channel with the id %s", clientEvent.Channel)), conn)
				}

				if !channel.IsPresenceOrPrivate() {
					emitWSError(newGenericError("Client event rejected - only supported on private and presence channels"), conn)
					break
				}

				if err := app.Publish(channel, clientEvent, sessionID); err != nil {
					log.Error(err)
					emitWSError(newGenericReconnectImmediatelyError(), conn)
					break
				}
			}

		} // switch
	} // For
}