// 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 }
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) }