Add --adduser flag to add users manually as a site operator.
このコミットが含まれているのは:
コミット
fbec273fb9
20
README.md
20
README.md
|
@ -10,18 +10,20 @@ space.
|
||||||
|
|
||||||
## Features (or lack thereof)
|
## Features (or lack thereof)
|
||||||
|
|
||||||
- users may register accounts mapped to individual twtxt feeds they may write to
|
- user accounts may be registered, mapped to individual twtxt feeds they can
|
||||||
|
write to
|
||||||
- no sessions, no cookies: few POST-writable resources (feeds, account data)
|
- no sessions, no cookies: few POST-writable resources (feeds, account data)
|
||||||
expect user credential parameters, which to store between requests is up to
|
expect user credential parameters, which to store between requests if desired
|
||||||
the user / browser
|
is up to the user / browser
|
||||||
|
- account registration may be open to the public, or closed (with the site
|
||||||
|
operator adding new accounts manually)
|
||||||
- users may register e-mail addresses and optional security questions to allow
|
- users may register e-mail addresses and optional security questions to allow
|
||||||
for password reset (if enabled by site operator)
|
for password reset (if enabled by site operator)
|
||||||
- account registration may be closed or open to the public
|
|
||||||
- HTTPS / TLS support (if paths to key and certificate files are provided)
|
- HTTPS / TLS support (if paths to key and certificate files are provided)
|
||||||
|
|
||||||
## Online demo
|
## Online demo
|
||||||
|
|
||||||
A demo instance with frequent downtimes can be tested at
|
A demo instance with frequent downtimes and open sign-up can be tested at
|
||||||
http://test.plomlompom.com:8000 (don't expect any of its feeds' URLs to be
|
http://test.plomlompom.com:8000 (don't expect any of its feeds' URLs to be
|
||||||
stable; it's just for testing, and data frequently gets deleted).
|
stable; it's just for testing, and data frequently gets deleted).
|
||||||
|
|
||||||
|
@ -76,10 +78,12 @@ This is [a common privilege problem](http://stackoverflow.com/q/413807) and
|
||||||
|
|
||||||
sudo setcap 'cap_net_bind_service=+ep' $GOPATH/bin/htwtxt
|
sudo setcap 'cap_net_bind_service=+ep' $GOPATH/bin/htwtxt
|
||||||
|
|
||||||
### Public sign-up
|
### Public or closed sign-up
|
||||||
|
|
||||||
By default, sign up / account creation is not open to the public. The `--signup`
|
By default, sign up / account creation is not open to the web-browsing public.
|
||||||
flag must be set explicitely to change that.
|
The `--signup` flag must be set explicitely to change that. Alternatively, new
|
||||||
|
accounts can be added by starting the program with the `--adduser` flag,
|
||||||
|
followed by an argument of the form `NAME:PASSWORD`.
|
||||||
|
|
||||||
### Set site owner contact info
|
### Set site owner contact info
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ func signUpHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := r.FormValue("name")
|
name := r.FormValue("name")
|
||||||
if "" == name || !onlyLegalRunes(name) || len(name) > 140 {
|
if !nameIsLegal(name) {
|
||||||
execTemplate(w, "error.html", "Illegal name.")
|
execTemplate(w, "error.html", "Illegal name.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
65
main.go
65
main.go
|
@ -117,19 +117,31 @@ func login(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
return name, nil
|
return name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nameIsLegal(name string) bool {
|
||||||
|
return !("" == name || !onlyLegalRunes(name) || len(name) > 140)
|
||||||
|
}
|
||||||
|
|
||||||
|
func passwordIsLegal(password string) bool {
|
||||||
|
return !("" == password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashFromPw(pw string) string {
|
||||||
|
hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Can't generate hash", err)
|
||||||
|
}
|
||||||
|
return string(hash)
|
||||||
|
}
|
||||||
|
|
||||||
func newPassword(w http.ResponseWriter, r *http.Request) (string, error) {
|
func newPassword(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
pw := r.FormValue("new_password")
|
pw := r.FormValue("new_password")
|
||||||
pw2 := r.FormValue("new_password2")
|
pw2 := r.FormValue("new_password2")
|
||||||
if 0 != strings.Compare(pw, pw2) {
|
if 0 != strings.Compare(pw, pw2) {
|
||||||
return "", errors.New("Password values did not match")
|
return "", errors.New("Password values did not match")
|
||||||
} else if "" == pw {
|
} else if !passwordIsLegal(pw) {
|
||||||
return "", errors.New("Illegal password.")
|
return "", errors.New("Illegal password.")
|
||||||
}
|
}
|
||||||
hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
|
return hashFromPw(pw), nil
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Can't generate password hash", err)
|
|
||||||
}
|
|
||||||
return string(hash), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMailAddress(w http.ResponseWriter, r *http.Request) (string, error) {
|
func newMailAddress(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
|
@ -152,12 +164,7 @@ func newSecurityQuestion(w http.ResponseWriter, r *http.Request) (string,
|
||||||
} else if "" == secanswer {
|
} else if "" == secanswer {
|
||||||
return "", "", errors.New("Illegal security question answer.")
|
return "", "", errors.New("Illegal security question answer.")
|
||||||
}
|
}
|
||||||
hash, err := bcrypt.GenerateFromPassword([]byte(secanswer),
|
return secquestion, hashFromPw(secanswer), nil
|
||||||
bcrypt.DefaultCost)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Can't generate security question answer hash", err)
|
|
||||||
}
|
|
||||||
return secquestion, string(hash), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeLoginField(w http.ResponseWriter, r *http.Request,
|
func changeLoginField(w http.ResponseWriter, r *http.Request,
|
||||||
|
@ -203,11 +210,14 @@ func nameMyself(ssl bool, port int) string {
|
||||||
return "http" + s + "://" + ip + ":" + strconv.Itoa(port)
|
return "http" + s + "://" + ip + ":" + strconv.Itoa(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readOptions() (string, int, string, int) {
|
func readOptions() (string, int, string, int, string) {
|
||||||
var mailpw string
|
var mailpw string
|
||||||
var mailport int
|
var mailport int
|
||||||
var mailserver string
|
var mailserver string
|
||||||
var port int
|
var port int
|
||||||
|
var newLogin string
|
||||||
|
flag.StringVar(&newLogin, "adduser", "", "instead of starting as "+
|
||||||
|
"server, add user with login NAME:PASSWORD")
|
||||||
flag.IntVar(&port, "port", 8000, "port to serve")
|
flag.IntVar(&port, "port", 8000, "port to serve")
|
||||||
flag.StringVar(&keyPath, "key", "", "SSL key file")
|
flag.StringVar(&keyPath, "key", "", "SSL key file")
|
||||||
flag.StringVar(&certPath, "cert", "", "SSL certificate file")
|
flag.StringVar(&certPath, "cert", "", "SSL certificate file")
|
||||||
|
@ -244,13 +254,38 @@ func readOptions() (string, int, string, int) {
|
||||||
mailpw = string(bytePassword)
|
mailpw = string(bytePassword)
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
}
|
}
|
||||||
return mailserver, mailport, mailpw, port
|
return mailserver, mailport, mailpw, port, newLogin
|
||||||
|
}
|
||||||
|
|
||||||
|
func addUser(login string) {
|
||||||
|
fields := strings.Split(login, ":")
|
||||||
|
if len(fields) != 2 {
|
||||||
|
log.Fatal("Malformed adduser string, must be NAME:PASSWORD")
|
||||||
|
}
|
||||||
|
name := fields[0]
|
||||||
|
password := fields[1]
|
||||||
|
if !nameIsLegal(name) {
|
||||||
|
log.Fatal("Malformed adduser NAME argument.")
|
||||||
|
}
|
||||||
|
if !passwordIsLegal(password) {
|
||||||
|
log.Fatal("Malformed adduser PASSWORD argument.")
|
||||||
|
}
|
||||||
|
if _, err := getFromFileEntryFor(loginsPath, name, 5); err == nil {
|
||||||
|
log.Fatal("Username already taken.")
|
||||||
|
}
|
||||||
|
hash := hashFromPw(password)
|
||||||
|
appendToFile(loginsPath, name+"\t"+hash+"\t\t\t")
|
||||||
|
fmt.Println("Added user.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
mailserver, mailport, mailpw, port := readOptions()
|
mailserver, mailport, mailpw, port, newLogin := readOptions()
|
||||||
initFilesAndDirs()
|
initFilesAndDirs()
|
||||||
|
if "" != newLogin {
|
||||||
|
addUser(newLogin)
|
||||||
|
return
|
||||||
|
}
|
||||||
myself = nameMyself("" != keyPath, port)
|
myself = nameMyself("" != keyPath, port)
|
||||||
templ, err = template.New("main").ParseGlob(templPath + "/*.html")
|
templ, err = template.New("main").ParseGlob(templPath + "/*.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
読み込み中…
新しいイシューから参照