예제 #1
0
파일: hawk.go 프로젝트: pdehaan/wmf
func (self *Hawk) genHash(req *http.Request, body string) (hash string) {
	contentType := req.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "text/plain"
	}
	// Something is appending the chartype to the content. this can throw off
	// the hash generator.
	// Client creates mac using "application/json",
	// we get "application/json;charset=UTF8" which brings much sadness.
	contentType = (strings.Split(contentType, ";"))[0]
	nbody := strings.Replace(body, "\\", "\\\\", -1)
	nbody = strings.Replace(nbody, "\n", "\\n", -1)
	marshalStr := fmt.Sprintf("%s\n%s\n%s\n",
		"hawk.1.payload",
		contentType,
		nbody)
	sha := sha256.Sum256([]byte(marshalStr))
	hash = base64.StdEncoding.EncodeToString([]byte(sha[:32]))
	if util.MzGetFlag(self.config, "hawk.show_hash") {
		self.logger.Debug("hawk", "genHash",
			util.Fields{"marshalStr": marshalStr,
				"hash": hash})
	}
	return hash

}
예제 #2
0
파일: handlers.go 프로젝트: hyl87/pushgo
func FixConfig(config util.JsMap) util.JsMap {
	if _, ok := config["shard.current_host"]; !ok {
		currentHost := "localhost"
		if val := os.Getenv("HOST"); len(val) > 0 {
			currentHost = val
		} else {
			if util.MzGetFlag(config, "shard.use_aws_host") {
				var awsHost string
				var err error
				awsHost, err = awsGetPublicHostname()
				if err == nil {
					currentHost = awsHost
				}
			}
		}
		config["shard.current_host"] = currentHost
	}
	// Convert the token_key from base64 (if present)
	if k, ok := config["token_key"]; ok {
		key, err := base64.URLEncoding.DecodeString(k.(string))
		if err != nil {
			log.Fatal(err)
		}
		config["token_key"] = key
	}

	DEFAULT_MAX_CONNECTIONS := 1000
	config["heka.current_host"] = config["shard.current_host"]
	if _, ok := config["max_connections"]; ok {
		var err error
		val := config["max_connections"].(string)
		ival, err := strconv.ParseInt(val, 10, 0)
		if err != nil {
			config["max_connections"] = DEFAULT_MAX_CONNECTIONS
		} else {
			config["max_connections"] = int(ival)
		}
	} else {
		config["max_connections"] = DEFAULT_MAX_CONNECTIONS
	}

	return config

}
예제 #3
0
파일: hawk.go 프로젝트: pdehaan/wmf
/* Things to check:
 * Are all values being sent? (e.g. extra, time, secret)
 * Do the secrets match?
 * is the other format string formatted correctly? (two \n before extra, 0 after)
 */
func (self *Hawk) GenerateSignature(req *http.Request, extra, body, secret string) (err error) {
	// create path
	if self.Path == "" {
		self.Path = getFullPath(req)
	}
	// figure out port
	if self.Host == "" {
		self.Host, self.Port = self.getHostPort(req)
	}
	if self.Nonce == "" {
		self.Nonce = GenNonce(6)
	}
	if self.Time == "" {
		self.Time = strconv.FormatInt(time.Now().UTC().Unix(), 10)
	}
	if self.Method == "" {
		self.Method = strings.ToUpper(req.Method)
	}
	if self.Hash == "" {
		self.Hash = self.genHash(req, body)
	}

	marshalStr := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
		"hawk.1.header",
		self.Time,
		self.Nonce,
		strings.ToUpper(self.Method),
		self.Path,
		strings.ToLower(self.Host),
		self.Port,
		self.Hash,
		extra)

	if util.MzGetFlag(self.config, "hawk.show_hash") {
		self.logger.Debug("hawk", "Marshal",
			util.Fields{"marshalStr": marshalStr,
				"secret": secret})
	}
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(marshalStr))
	self.Signature = base64.StdEncoding.EncodeToString(mac.Sum(nil))
	return err
}
예제 #4
0
파일: handlers.go 프로젝트: pdehaan/wmf
// verify a Persona assertion using the config values
// part of Handler for config & logging reasons
func (self *Handler) verifyAssertion(assertion string) (userid, email string, err error) {
	var ok bool
	if util.MzGetFlag(self.config, "auth.disabled") {
		self.logger.Info(self.logCat, "### Skipping validation...", nil)
		return "user1", "*****@*****.**", nil
	}
	ver_url := util.MzGet(self.config, "persona.validater_url", "https://verifier.login.persona.org/verify")
	audience := util.MzGet(self.config, "persona.audience",
		"http://localhost:8080")
	res, err := http.PostForm(ver_url, url.Values{
		"assertion": {assertion},
		"audience":  {audience}})
	if err != nil {
		self.logger.Error(self.logCat, "Persona verification failed",
			util.Fields{"error": err.Error()})
		return "", "", AuthorizationErr
	}
	buffer, err := parseBody(res.Body)
	if isOk, ok := buffer["status"]; !ok || isOk != "okay" {
		var errStr string
		if err != nil {
			errStr = err.Error()
		} else if _, ok = buffer["reason"]; ok {
			errStr = buffer["reason"].(string)
		}
		self.logger.Error(self.logCat, "Persona Auth Failed",
			util.Fields{"error": errStr,
				"buffer": fmt.Sprintf("%+v", buffer)})
		return "", "", AuthorizationErr
	}
	if email, ok = buffer["email"].(string); !ok {
		self.logger.Error(self.logCat, "No email found in assertion",
			util.Fields{"assertion": fmt.Sprintf("%+v", buffer)})
		return "", "", AuthorizationErr
	}
	if userid, ok = buffer["userid"].(string); !ok {
		hasher := sha256.New()
		hasher.Write([]byte(email))
		userid = hex.EncodeToString(hasher.Sum(nil))
	}
	return userid, email, nil
}
예제 #5
0
파일: hawk.go 프로젝트: pdehaan/wmf
// get the host and port from the request
func (self *Hawk) getHostPort(req *http.Request) (host, port string) {

	elements := strings.Split(req.Host, ":")
	host = elements[0]
	if len(elements) == 2 {
		port = elements[1]
	}
	if port == "" || util.MzGetFlag(self.config, "override_port") {
		switch {
		// because nginx proxies, don't take the :port at face value
		//case len(elements) > 1:
		//	port = elements[1]
		case req.URL.Scheme == "https":
			port = "443"
		default:
			port = "80"
		}
	}
	return host, port
}
예제 #6
0
파일: worker.go 프로젝트: hyl87/pushgo
func (self *Worker) Ping(sock *PushWS, buffer interface{}) (err error) {
	if self.pingInt > 0 && int64(self.lastPing.Sub(time.Now()).Seconds()) < self.pingInt {
		source := sock.Socket.Config().Origin
		if self.logger != nil {
			self.logger.Error("worker",
				fmt.Sprintf("Client sending too many pings %s", source), nil)
		} else {
			log.Printf("Worker: Client sending too many pings. %s", source)
		}
		self.stopped = true
		return sperrors.TooManyPingsError
	}
	data := buffer.(util.JsMap)
	if util.MzGetFlag(self.config, "push.long_pongs") {
		websocket.JSON.Send(sock.Socket, util.JsMap{
			"messageType": data["messageType"],
			"status":      200})
	} else {
		websocket.Message.Send(sock.Socket, "{}")
	}
	return nil
}
예제 #7
0
파일: main.go 프로젝트: pdehaan/wmf
func main() {
	flags.ParseArgs(&opts, os.Args)

	// Configuration
	// defaults don't appear to work.
	if opts.ConfigFile == "" {
		opts.ConfigFile = "config.ini"
	}
	config := util.MzGetConfig(opts.ConfigFile)
	config["VERSION"] = VERSION
	if util.MzGetFlag(config, "aws.get_hostname") {
		if hostname, err := util.GetAWSPublicHostname(); err == nil {
			config["ws_hostname"] = hostname
		}
	}

	//TODO: Build out the partner cert pool if need be.
	// certpoo

	if opts.Profile != "" {
		log.Printf("Creating profile %s...\n", opts.Profile)
		f, err := os.Create(opts.Profile)
		if err != nil {
			log.Fatal("Profile creation failed:\n%s\n", err.Error())
			return
		}
		defer func() {
			log.Printf("Closing profile...\n")
			pprof.StopCPUProfile()
		}()
		pprof.StartCPUProfile(f)
	}
	if opts.MemProfile != "" {
		defer func() {
			profFile, err := os.Create(opts.MemProfile)
			if err != nil {
				log.Fatal("Memory Profile creation failed:\n%s\n", err.Error())
				return
			}
			pprof.WriteHeapProfile(profFile)
			profFile.Close()
		}()
	}

	runtime.GOMAXPROCS(runtime.NumCPU())
	logger := util.NewHekaLogger(config)
	store, err := storage.Open(config, logger)
	if err != nil {
		logger.Error("main", "FAIL", nil)
		return
	}
	handlers := wmf.NewHandler(config, logger, store)

	// Signal handler
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGHUP, SIGUSR1)

	// Rest Config
	errChan := make(chan error)
	host := util.MzGet(config, "host", "localhost")
	port := util.MzGet(config, "port", "8080")

	var RESTMux *http.ServeMux = http.DefaultServeMux
	var WSMux *http.ServeMux = http.DefaultServeMux
	var verRoot = strings.SplitN(VERSION, ".", 2)[0]

	// REST calls
	// Device calls.
	RESTMux.HandleFunc(fmt.Sprintf("/%s/register/", verRoot),
		handlers.Register)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/cmd/", verRoot),
		handlers.Cmd)
	// Web UI calls
	RESTMux.HandleFunc(fmt.Sprintf("/%s/queue/", verRoot),
		handlers.Queue)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/state/", verRoot),
		handlers.State)
	RESTMux.HandleFunc("/static/",
		handlers.Static)
	RESTMux.HandleFunc("/metrics/",
		handlers.Metrics)
	// Operations call
	RESTMux.HandleFunc("/status/",
		handlers.Status)
	WSMux.Handle(fmt.Sprintf("/%s/ws/", verRoot),
		websocket.Handler(handlers.WSSocketHandler))
	// Handle root calls as webUI
	RESTMux.HandleFunc("/",
		handlers.Index)

	logger.Info("main", "startup...",
		util.Fields{"host": host, "port": port})

	go func() {
		errChan <- http.ListenAndServe(host+":"+port, nil)
	}()

	select {
	case err := <-errChan:
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	case <-sigChan:
		logger.Info("main", "Shutting down...", nil)
	}

}
예제 #8
0
파일: handlers.go 프로젝트: hyl87/pushgo
// -- REST
func (self *Handler) UpdateHandler(resp http.ResponseWriter, req *http.Request) {
	// Handle the version updates.
	var err error
	var port string
	var vers int64

	if ClientCount() > self.config["max_connections"].(int) {
		if self.logger != nil {
			if toomany == 0 {
				atomic.StoreInt32(&toomany, 1)
				self.logger.Error("handler", "Socket Count Exceeded", nil)
			}
		}
		MetricIncrement("too many connections")
		http.Error(resp, "{\"error\": \"Server unavailable\"}",
			http.StatusServiceUnavailable)
		return
	}
	if toomany != 0 {
		atomic.StoreInt32(&toomany, 0)
	}

	timer := time.Now()
	filter := regexp.MustCompile("[^\\w-\\.\\=]")
	if self.logger != nil {
		self.logger.Debug("update", "Handling Update",
			util.Fields{"path": req.URL.Path})
	}
	if self.logger != nil {
		self.logger.Debug("update", "=========== UPDATE ====", nil)
	}

	defer func() {
		if self.logger != nil {
			self.logger.Debug("update", "+++++++++++++ DONE +++", nil)
		}
	}()
	if req.Method != "PUT" {
		http.Error(resp, "", http.StatusMethodNotAllowed)
		return
	}

	svers := req.FormValue("version")
	if svers != "" {
		vers, err = strconv.ParseInt(svers, 10, 64)
		if err != nil || vers < 0 {
			http.Error(resp, "\"Invalid Version\"", http.StatusBadRequest)
			return
		}
	} else {
		vers = time.Now().UTC().Unix()
	}

	elements := strings.Split(req.URL.Path, "/")
	pk := elements[len(elements)-1]
	if len(pk) == 0 {
		if self.logger != nil {
			self.logger.Error("update", "No token, rejecting request",
				util.Fields{"remoteAddr": req.RemoteAddr,
					"path": req.URL.Path})
		}
		http.Error(resp, "Token not found", http.StatusNotFound)
		return
	}

	if token, ok := self.config["token_key"]; ok && len(token.([]uint8)) > 0 {
		if self.logger != nil {
			// Note: dumping the []uint8 keys can produce terminal glitches
			self.logger.Debug("main", "Decoding...", nil)
		}
		var err error
		bpk, err := Decode(token.([]byte),
			pk)
		if err != nil {
			if self.logger != nil {
				self.logger.Error("update",
					"Could not decode token",
					util.Fields{"primarykey": pk,
						"remoteAddr": req.RemoteAddr,
						"path":       req.URL.Path,
						"error":      ErrStr(err)})
			}
			http.Error(resp, "", http.StatusNotFound)
			return
		}

		pk = strings.TrimSpace(string(bpk))
	}

	if filter.Find([]byte(pk)) != nil {
		if self.logger != nil {
			self.logger.Error("update",
				"Invalid token for update",
				util.Fields{"token": pk,
					"path": req.URL.Path})
		}
		http.Error(resp, "Invalid Token", http.StatusNotFound)
		return
	}

	uaid, chid, err := storage.ResolvePK(pk)
	if err != nil {
		if self.logger != nil {
			self.logger.Error("update",
				"Could not resolve PK",
				util.Fields{"primaryKey": pk,
					"path":  req.URL.Path,
					"error": ErrStr(err)})
		}
		return
	}

	if chid == "" {
		if self.logger != nil {
			self.logger.Error("update",
				"Incomplete primary key",
				util.Fields{"uaid": uaid,
					"channelID":  chid,
					"remoteAddr": req.RemoteAddr})
		}
		return
	}

	//log.Printf("<< %s.%s = %d", uaid, chid, vers)

	if iport, ok := self.config["port"]; ok {
		port = iport.(string)
	}
	if port == "80" {
		port = ""
	}
	if port != "" {
		port = ":" + port
	}
	currentHost := util.MzGet(self.config, "shard.current_host", "localhost")
	host, err := self.store.GetUAIDHost(uaid)
	if err != nil {
		if self.logger != nil {
			self.logger.Error("update",
				"Could not discover host for UAID",
				util.Fields{"uaid": uaid,
					"error": err.Error()})
		}
		host = util.MzGet(self.config, "shard.defaultHost", "localhost")
	}
	if util.MzGetFlag(self.config, "shard.do_proxy") {
		if host != currentHost && host != "localhost" {
			if self.logger != nil {
				self.logger.Info("update",
					"Proxying request for UAID",
					util.Fields{"uaid": uaid,
						"destination": host + port})
			}
			// Use tcp routing.
			if util.MzGetFlag(self.config, "shard.router") {
				// If there was an error routing the update, don't
				// tell the AppServer. Chances are it's temporary, and
				// the client will get the update on next refresh/reconnect
				self.router.SendUpdate(host, uaid, chid, vers, timer)
			} else {
				proto := "http"
				if len(util.MzGet(self.config, "ssl.certfile", "")) > 0 {
					proto = "https"
				}

				err = proxyNotification(proto, host+port, req.URL.Path, vers)
				if err != nil && self.logger != nil {
					self.logger.Error("update",
						"Proxy failed", util.Fields{
							"uaid":        uaid,
							"destination": host + port,
							"error":       err.Error()})
				}
			}
			MetricIncrement("routing update: out")
			if err != nil {
				http.Error(resp, err.Error(), 500)
			} else {
				resp.Write([]byte("Ok"))
			}
			return
		}
	}

	if self.logger != nil {
		defer func(uaid, chid, path string, timer time.Time) {
			self.logger.Info("timer", "Client Update complete",
				util.Fields{
					"uaid":      uaid,
					"path":      req.URL.Path,
					"channelID": chid,
					"duration":  strconv.FormatInt(time.Now().Sub(timer).Nanoseconds(), 10)})
		}(uaid, chid, req.URL.Path, timer)

		self.logger.Info("update",
			"setting version for ChannelID",
			util.Fields{"uaid": uaid, "channelID": chid,
				"version": strconv.FormatInt(vers, 10)})
	}
	err = self.store.UpdateChannel(pk, vers)

	if err != nil {
		if self.logger != nil {
			self.logger.Error("update", "Cound not update channel",
				util.Fields{"UAID": uaid,
					"channelID": chid,
					"version":   strconv.FormatInt(vers, 10),
					"error":     err.Error()})
		}
		status, _ := sperrors.ErrToStatus(err)
		http.Error(resp, "Could not update channel version", status)
		return
	}
	resp.Header().Set("Content-Type", "application/json")
	resp.Write([]byte("{}"))
	// Ping the appropriate server
	if client, ok := Clients[uaid]; ok {
		Flush(client, chid, int64(vers))
	}
	MetricIncrement("update channel")
	return
}
예제 #9
0
파일: handlers.go 프로젝트: pdehaan/wmf
func (self *Handler) Cmd(resp http.ResponseWriter, req *http.Request) {
	/* Log response and Pass the latest command off to the device.
	 */
	var err error
	var l int

	self.logCat = "handler:Cmd"
	resp.Header().Set("Content-Type", "application/json")
	deviceId := getDevFromUrl(req)
	if deviceId == "" {
		self.logger.Error(self.logCat, "Invalid call (No device id)", nil)
		http.Error(resp, "Unauthorized", 401)
		return
	}

	devRec, err := self.store.GetDeviceInfo(deviceId)
	if err != nil {
		switch err {
		case storage.ErrUnknownDevice:
			self.logger.Error(self.logCat,
				"Cmd:Unknown device requesting cmd",
				util.Fields{
					"deviceId": deviceId})
			http.Error(resp, "Unauthorized", 401)
		default:
			self.logger.Error(self.logCat,
				"Cmd:Unhandled Error",
				util.Fields{
					"error":    err.Error(),
					"deviceId": deviceId})
			http.Error(resp, "Unauthorized", 401)
		}
		return
	}
	//decode the body
	var body = make([]byte, req.ContentLength)
	l, err = req.Body.Read(body)
	if err != nil {
		self.logger.Error(self.logCat, "Could not read body",
			util.Fields{"error": err.Error()})
		http.Error(resp, "Invalid", 400)
		return
	}
	//validate the Hawk header
	if util.MzGetFlag(self.config, "hawk.disabled") == false {
		// Remote Hawk
		rhawk := Hawk{logger: self.logger}
		// Local Hawk
		lhawk := Hawk{logger: self.logger}
		// Get the remote signature from the header
		err = rhawk.ParseAuthHeader(req, self.logger)
		if err != nil {
			self.logger.Error(self.logCat, "Could not parse Hawk header",
				util.Fields{"error": err.Error()})
			http.Error(resp, "Unauthorized", 401)
			return
		}

		// Generate the comparator signature from what we know.
		lhawk.Nonce = rhawk.Nonce
		lhawk.Time = rhawk.Time
		//lhawk.Hash = rhawk.Hash

		err = lhawk.GenerateSignature(req, rhawk.Extra, string(body),
			devRec.Secret)
		if err != nil {
			self.logger.Error(self.logCat, "Could not verify sig",
				util.Fields{"error": err.Error()})
			http.Error(resp, "Unauthorized", 401)
			return
		}
		// Do they match?
		if !lhawk.Compare(rhawk.Signature) {
			self.logger.Error(self.logCat, "Cmd:Invalid Hawk Signature",
				util.Fields{
					"expecting": lhawk.Signature,
					"got":       rhawk.Signature,
				})
			http.Error(resp, "Unauthorized", 401)
			return
		}
	}
	// Do the command.
	self.logger.Info(self.logCat, "Handling cmd",
		util.Fields{
			"cmd":    string(body),
			"length": fmt.Sprintf("%d", l),
		})
	if l > 0 {
		reply := make(reply_t)
		merr := json.Unmarshal(body, &reply)
		//	merr := json.Unmarshal(body, &reply)
		if merr != nil {
			self.logger.Error(self.logCat,
				"Could not unmarshal data",
				util.Fields{
					"error": merr.Error(),
					"body":  string(body)})
			http.Error(resp, "Server Error", 500)
			return
		}

		for cmd, args := range reply {
			c := strings.ToLower(string(cmd[0]))
			if !strings.Contains(devRec.Accepts, c) {
				self.logger.Warn(self.logCat, "Unacceptable Command",
					util.Fields{"unacceptable": c,
						"acceptable": devRec.Accepts})
				continue
			}
			self.metrics.Increment("cmd.received." + string(c))
			switch c {
			case "l", "r", "m", "e":
				err = self.store.Touch(deviceId)
				self.updatePage(deviceId, args.(map[string]interface{}), false)
			case "h":
				argl := make(reply_t)
				argl[string(cmd)] = isTrue(args)
				self.updatePage(deviceId, argl, false)
			case "t":
				// track
				err = self.updatePage(deviceId, args.(map[string]interface{}), true)
				// store tracking info.
			case "q":
				// User has quit, nuke what we know.
				if util.MzGetFlag(self.config, "cmd.q.allow") {
					err = self.store.DeleteDevice(deviceId)
				}
			}
			if err != nil {
				// Log the error
				self.logger.Error(self.logCat, "Error handling command",
					util.Fields{"error": err.Error(),
						"command": string(cmd),
						"device":  deviceId,
						"args":    fmt.Sprintf("%v", args)})
				http.Error(resp,
					"Server Error",
					http.StatusServiceUnavailable)
				return
			}
		}
	}

	// reply with pending commands
	//
	cmd, err := self.store.GetPending(deviceId)
	var output []byte = []byte(cmd)
	if err != nil {
		self.logger.Error(self.logCat, "Could not send commands",
			util.Fields{"error": err.Error()})
		http.Error(resp, "Server Error", http.StatusServiceUnavailable)
	}
	if output == nil || len(output) < 2 {
		output = []byte("{}")
	}
	authHeader := self.hawk.AsHeader(req, devRec.User, string(output),
		"", devRec.Secret)
	resp.Header().Add("Authorization", authHeader)
	// total cheat to get the command without parsing the cmd data.
	if len(cmd) > 2 {
		self.metrics.Increment("cmd.send." + string(cmd[2]))
	}
	resp.Write(output)
}