예제 #1
0
// Use one of the databases for the permission middleware,
// assign a name to dbName (used for the status output) and
// return a Permissions struct.
func mustAquirePermissions() pinterface.IPermissions {
	var (
		err  error
		perm pinterface.IPermissions
	)

	// If Bolt is to be used and no filename is given
	if useBolt && (boltFilename == "") {
		boltFilename = defaultBoltFilename
	}

	if boltFilename != "" {
		// New permissions middleware, using a Bolt database
		perm, err = bolt.NewWithConf(boltFilename)
		if err != nil {
			if err.Error() == "timeout" {
				tempFile, err := ioutil.TempFile("", "algernon")
				if err != nil {
					log.Fatal("Unable to find a temporary file to use:", err)
				} else {
					boltFilename = tempFile.Name() + ".db"
				}
			} else {
				log.Errorf("Could not use Bolt as database backend: %s", err)
			}
		} else {
			dbName = "Bolt (" + boltFilename + ")"
		}
		// Try the new database filename if there was a timeout
		if dbName == "" && boltFilename != defaultBoltFilename {
			perm, err = bolt.NewWithConf(boltFilename)
			if err != nil {
				if err.Error() == "timeout" {
					log.Error("The Bolt database timed out!")
				} else {
					log.Errorf("Could not use Bolt as database backend: %s", err)
				}
			} else {
				dbName = "Bolt, temporary"
			}
		}
	}
	if dbName == "" && mariadbDSN != "" {
		// New permissions middleware, using a MariaDB/MySQL database
		perm, err = mariadb.NewWithDSN(mariadbDSN, mariaDatabase)
		if err != nil {
			log.Errorf("Could not use MariaDB/MySQL as database backend: %s", err)
		} else {
			// The connection string may contain a password, so don't include it in the dbName
			dbName = "MariaDB/MySQL"
		}
	}
	if dbName == "" && mariaDatabase != "" {
		// Given a database, but not a host, connect to localhost
		// New permissions middleware, using a MariaDB/MySQL database
		perm, err = mariadb.NewWithConf("test:@127.0.0.1/" + mariaDatabase)
		if err != nil {
			if mariaDatabase != "" {
				log.Errorf("Could not use MariaDB/MySQL as database backend: %s", err)
			} else {
				log.Warnf("Could not use MariaDB/MySQL as database backend: %s", err)
			}
		} else {
			// The connection string may contain a password, so don't include it in the dbName
			dbName = "MariaDB/MySQL"
		}
	}
	if dbName == "" {
		// New permissions middleware, using a Redis database
		if err := simpleredis.TestConnectionHost(redisAddr); err != nil {
			// Only output an error when a Redis host other than the default host+port was specified
			if redisAddrSpecified {
				if singleFileMode {
					log.Warnf("Could not use Redis as database backend: %s", err)
				} else {
					log.Errorf("Could not use Redis as database backend: %s", err)
				}
			}
		} else {
			perm = redis.NewWithRedisConf(redisDBindex, redisAddr)
			dbName = "Redis"
		}
	}
	if dbName == "" && boltFilename == "" {
		boltFilename = defaultBoltFilename
		perm, err = bolt.NewWithConf(boltFilename)
		if err != nil {
			if err.Error() == "timeout" {
				tempFile, err := ioutil.TempFile("", "algernon")
				if err != nil {
					log.Fatal("Unable to find a temporary file to use:", err)
				} else {
					boltFilename = tempFile.Name() + ".db"
				}
			} else {
				log.Errorf("Could not use Bolt as database backend: %s", err)
			}
		} else {
			dbName = "Bolt (" + boltFilename + ")"
		}
		// Try the new database filename if there was a timeout
		if boltFilename != defaultBoltFilename {
			perm, err = bolt.NewWithConf(boltFilename)
			if err != nil {
				if err.Error() == "timeout" {
					log.Error("The Bolt database timed out!")
				} else {
					log.Errorf("Could not use Bolt as database backend: %s", err)
				}
			} else {
				dbName = "Bolt, temporary"
			}
		}
	}
	if dbName == "" {
		// This may typically happen if Algernon is already running
		log.Fatalln("Could not find a usable database backend.")
	}

	return perm
}
예제 #2
0
func main() {
	router := mux.NewRouter()

	r := render.New()

	// New permissions middleware
	perm, _ := permissions.NewWithConf(configPath + "bolt.db")

	perm.AddUserPath("/api/holes/")
	perm.AddUserPath("/api/new_ca/")
	perm.AddUserPath("/api/new_cert/")
	perm.AddUserPath("/api/ca.pem")
	perm.AddUserPath("/api/ca.key")
	perm.AddUserPath("/api/cert.pem")
	perm.AddUserPath("/api/cert.key")

	// Get the userstate, used in the handlers below
	userstate := perm.UserState()

	creator := userstate.Creator()
	emails, _ := creator.NewKeyValue("emails")
	passwordTokens, _ := creator.NewKeyValue("password_tokens")
	usershole := NewUsersHole(userstate)

	router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Hello HoleHub.")
	})

	router.HandleFunc("/api/signup/", func(w http.ResponseWriter, req *http.Request) {
		userForm := new(NewUserForm)
		errs := binding.Bind(req, userForm)
		if errs.Handle(w) {
			return
		}
		if userstate.HasUser(userForm.Name) {
			r.JSON(w, http.StatusOK, ErrorMessages[1])
			return
		}
		if name, _ := emails.Get(userForm.Email); name != "" {
			r.JSON(w, http.StatusOK, ErrorMessages[2])
			return
		}
		if !isEmail(userForm.Email) {
			r.JSON(w, http.StatusOK, ErrorMessages[3])
			return
		}
		userstate.AddUser(userForm.Name, userForm.Password, userForm.Email)
		emails.Set(userForm.Email, userForm.Name)
		GenerateUserCa(userForm.Name)
		GenerateUserCert(userForm.Name)
		users := userstate.Users()
		ca := userForm.Name + "-ca.pem"
		cakey := userForm.Name + "-ca.key"
		cert := userForm.Name + "-cert.pem"
		certkey := userForm.Name + "-cert.key"
		users.Set(userForm.Name, "ca", ca)
		users.Set(userForm.Name, "cakey", cakey)
		users.Set(userForm.Name, "cert", cert)
		users.Set(userForm.Name, "certkey", certkey)

		code, _ := userstate.GenerateUniqueConfirmationCode()
		userstate.AddUnconfirmed(userForm.Name, code)
		SendConfirmationCode(userForm.Name, userForm.Email, code)

		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/signin/", func(w http.ResponseWriter, req *http.Request) {
		authForm := new(AuthForm)
		errs := binding.Bind(req, authForm)
		if errs.Handle(w) {
			return
		}
		name := authForm.NameOrEmail
		if isEmail(authForm.NameOrEmail) {
			name, _ = emails.Get(authForm.NameOrEmail)
		}
		if !userstate.CorrectPassword(name, authForm.Password) {
			r.JSON(w, http.StatusOK, ErrorMessages[4])
			return
		}
		userstate.Login(w, name)
		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/ping/", func(w http.ResponseWriter, req *http.Request) {
		var pong = []byte("false")
		if userstate.UserRights(req) {
			pong = []byte("true")
		}
		r.Data(w, http.StatusOK, pong)
	}).Methods("GET")

	router.HandleFunc("/api/holes/create/", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		req.ParseForm()
		scheme := req.Form.Get("scheme")
		holeName := req.Form.Get("name")

		hs := usershole.NewHoleApp(username, holeName, scheme)
		r.JSON(w, http.StatusOK, map[string]HoleApp{"hole": *hs})
	}).Methods("POST")

	router.HandleFunc("/api/holes/{holeID}/start/", func(w http.ResponseWriter, req *http.Request) {
		holeID := mux.Vars(req)["holeID"]
		username := userstate.Username(req)
		hs := usershole.GetOne(username, holeID)
		if hs == nil {
			r.JSON(w, http.StatusNotFound, ErrorMessages[10])
			return
		}
		hs.Start()
		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/holes/{holeID}/kill/", func(w http.ResponseWriter, req *http.Request) {
		holeID := mux.Vars(req)["holeID"]
		username := userstate.Username(req)
		hs := usershole.GetOne(username, holeID)
		if hs == nil {
			hs = &HoleApp{ID: holeID}
		}
		hs.Kill()
		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/holes/{holeID}/remove/", func(w http.ResponseWriter, req *http.Request) {
		holeID := mux.Vars(req)["holeID"]
		username := userstate.Username(req)
		if err := usershole.Remove(username, holeID); err != nil {
			r.JSON(w, http.StatusNotFound, ErrorMessages[10])
			return
		}
		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/holes/{holeID}/", func(w http.ResponseWriter, req *http.Request) {
		holeID := mux.Vars(req)["holeID"]
		username := userstate.Username(req)
		hs := usershole.GetOne(username, holeID)
		if hs == nil {
			r.JSON(w, http.StatusNotFound, ErrorMessages[10])
			return
		}
		r.JSON(w, http.StatusOK, hs)
	}).Methods("GET")

	router.HandleFunc("/api/holes/", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		holes := usershole.GetAll(username)
		r.JSON(w, http.StatusOK, map[string][]*HoleApp{"holes": holes})
	}).Methods("GET")

	router.HandleFunc("/api/new_ca/", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		GenerateUserCa(username)
		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/ca.pem", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		data, _ := ioutil.ReadFile(configPath + "certs/" + username + "-ca.pem")
		r.Data(w, http.StatusOK, data)
	}).Methods("GET")

	router.HandleFunc("/api/ca.key", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		data, _ := ioutil.ReadFile(configPath + "certs/" + username + "-ca.key")
		r.Data(w, http.StatusOK, data)
	}).Methods("GET")

	router.HandleFunc("/api/new_cert/", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		GenerateUserCert(username)
		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/cert.pem", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		data, _ := ioutil.ReadFile(configPath + "certs/" + username + "-cert.pem")
		r.Data(w, http.StatusOK, data)
	}).Methods("GET")

	router.HandleFunc("/api/cert.key", func(w http.ResponseWriter, req *http.Request) {
		username := userstate.Username(req)
		data, _ := ioutil.ReadFile(configPath + "certs/" + username + "-cert.key")
		r.Data(w, http.StatusOK, data)
	}).Methods("GET")

	router.HandleFunc("/api/confirm/{confirmationCode}", func(w http.ResponseWriter, req *http.Request) {
		code := mux.Vars(req)["confirmationCode"]
		if err := userstate.ConfirmUserByConfirmationCode(code); err != nil {
			r.JSON(w, http.StatusOK, ErrorMessages[5])
			return
		}
		msg := ErrorMessages[0]
		r.JSON(w, http.StatusOK, msg)
	}).Methods("GET")

	router.HandleFunc("/api/resend/confirmationcode", func(w http.ResponseWriter, req *http.Request) {
		req.ParseForm()
		email := req.Form.Get("email")
		username, _ := emails.Get(email)

		if username == "" {
			r.JSON(w, http.StatusOK, ErrorMessages[7])
			return
		}

		if userstate.IsConfirmed(username) {
			r.JSON(w, http.StatusOK, ErrorMessages[6])
			return
		}

		code, _ := userstate.GenerateUniqueConfirmationCode()
		userstate.AddUnconfirmed(username, code)

		SendConfirmationCode(username, email, code)
		msg := ErrorMessages[0]
		r.JSON(w, http.StatusOK, msg)
	}).Methods("POST")

	router.HandleFunc("/api/reset_password/", func(w http.ResponseWriter, req *http.Request) {
		resetPasswordForm := new(ResetPasswordForm)
		errs := binding.Bind(req, resetPasswordForm)
		if errs.Handle(w) {
			return
		}
		var username string
		if userstate.UserRights(req) {
			username = userstate.Username(req)
			if !userstate.CorrectPassword(username, resetPasswordForm.OldPassword) {
				r.JSON(w, http.StatusOK, ErrorMessages[8])
				return
			}
		} else if resetPasswordForm.Token != "" {
			tokenStr, _ := passwordTokens.Get(resetPasswordForm.Token)
			passwordTokens.Del(resetPasswordForm.Token)
			var token map[string]string
			if err := json.Unmarshal([]byte(tokenStr), &token); err != nil {
				r.JSON(w, http.StatusOK, ErrorMessages[9])
				return
			}
			current := int(time.Now().Unix())
			expiredAt, _ := strconv.Atoi(token["expiredAt"])
			if expiredAt < current {
				r.JSON(w, http.StatusOK, ErrorMessages[9])
				return
			}
			username = token["username"]
			if !userstate.HasUser(username) {
				r.JSON(w, http.StatusOK, ErrorMessages[7])
				return
			}
		} else {
			http.Error(w, "Permission denied!", http.StatusForbidden)
		}
		users := userstate.Users()
		passwordHash := userstate.HashPassword(username, resetPasswordForm.NewPassword)
		users.Set(username, "password", passwordHash)
		r.JSON(w, http.StatusOK, ErrorMessages[0])
	}).Methods("POST")

	router.HandleFunc("/api/send/passwordToken", func(w http.ResponseWriter, req *http.Request) {
		req.ParseForm()
		username := req.Form.Get("username")
		if isEmail(username) {
			username, _ = emails.Get(username)
		}
		if !userstate.HasUser(username) {
			r.JSON(w, http.StatusOK, ErrorMessages[7])
			return
		}

		loop := 0
		var code string
		for {
			code, _ := userstate.GenerateUniqueConfirmationCode()
			tokenStr, _ := passwordTokens.Get(code)
			if tokenStr == "" {
				break
			}
			loop = loop + 1
			if loop > 1000 {
				http.Error(w, "Too many loops...", http.StatusInternalServerError)
				return
			}
		}

		email, _ := userstate.Email(username)
		expiredAt := time.Now().Add(12 * time.Hour).Unix()
		passwordTokens.Set(code, fmt.Sprintf("{\"username\": \"%s\", \"expiredAt\": \"%d\"}", username, expiredAt))

		SendPasswordToken(username, email, code)
		msg := ErrorMessages[0]
		r.JSON(w, http.StatusOK, msg)
	}).Methods("POST")

	// Custom handler for when permissions are denied
	perm.SetDenyFunction(func(w http.ResponseWriter, req *http.Request) {
		http.Error(w, "Permission denied!", http.StatusForbidden)
	})

	n := negroni.Classic()

	n.Use(perm)
	n.Use(cors.NewAllow(&cors.Options{AllowAllOrigins: true}))
	n.UseHandler(router)

	//n.Run(":3000")
	fmt.Printf("HoleHUB is run on http://%s:%d\n", host, port)
	graceful.Run(fmt.Sprintf("%s:%d", host, port), 10*time.Second, n)
}