diff --git a/http_auth.go b/http_auth.go new file mode 100644 index 0000000..2e1039b --- /dev/null +++ b/http_auth.go @@ -0,0 +1,62 @@ +package main + +import ( + "log" + "net/http" + + "github.com/bouncepaw/mycorrhiza/templates" + "github.com/bouncepaw/mycorrhiza/user" +) + +func init() { + http.HandleFunc("/login", handlerLogin) + http.HandleFunc("/login-data", handlerLoginData) + http.HandleFunc("/logout", handlerLogout) + http.HandleFunc("/logout-confirm", handlerLogoutConfirm) +} + +func handlerLogout(w http.ResponseWriter, rq *http.Request) { + var ( + u = user.FromRequest(rq) + can = u != nil + ) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + if can { + log.Println("User", u.Name, "tries to log out") + w.WriteHeader(http.StatusOK) + } else { + log.Println("Unknown user tries to log out") + w.WriteHeader(http.StatusForbidden) + } + w.Write([]byte(base("Logout?", templates.LogoutHTML(can)))) +} + +func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) { + user.LogoutFromRequest(w, rq) + http.Redirect(w, rq, "/", http.StatusSeeOther) +} + +func handlerLoginData(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + var ( + username = CanonicalName(rq.PostFormValue("username")) + password = rq.PostFormValue("password") + err = user.LoginDataHTTP(w, rq, username, password) + ) + if err != "" { + w.Write([]byte(base(err, templates.LoginErrorHTML(err)))) + } else { + http.Redirect(w, rq, "/", http.StatusSeeOther) + } +} + +func handlerLogin(w http.ResponseWriter, rq *http.Request) { + log.Println(rq.URL) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + if user.AuthUsed { + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusForbidden) + } + w.Write([]byte(base("Login", templates.LoginHTML()))) +} diff --git a/main.go b/main.go index 9004c6e..4002cf1 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,6 @@ import ( "github.com/bouncepaw/mycorrhiza/history" "github.com/bouncepaw/mycorrhiza/templates" - "github.com/bouncepaw/mycorrhiza/user" "github.com/bouncepaw/mycorrhiza/util" ) @@ -110,31 +109,6 @@ func handlerStyle(w http.ResponseWriter, rq *http.Request) { } } -func handlerLoginData(w http.ResponseWriter, rq *http.Request) { - log.Println(rq.URL) - var ( - username = CanonicalName(rq.PostFormValue("username")) - password = rq.PostFormValue("password") - err = user.LoginDataHTTP(w, rq, username, password) - ) - if err != "" { - w.Write([]byte(base(err, templates.LoginErrorHTML(err)))) - } else { - http.Redirect(w, rq, "/", http.StatusSeeOther) - } -} - -func handlerLogin(w http.ResponseWriter, rq *http.Request) { - log.Println(rq.URL) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - if user.AuthUsed { - w.WriteHeader(http.StatusOK) - } else { - w.WriteHeader(http.StatusForbidden) - } - w.Write([]byte(base("Login", templates.LoginHTML()))) -} - func main() { log.Println("Running MycorrhizaWiki β") parseCliArgs() @@ -151,6 +125,7 @@ func main() { http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static")))) // See http_readers.go for /page/, /text/, /binary/, /history/. // See http_mutators.go for /upload-binary/, /upload-text/, /edit/, /delete-ask/, /delete-confirm/, /rename-ask/, /rename-confirm/. + // See http_auth.go for /login, /login-data, /logout, /logout-confirm http.HandleFunc("/list", handlerList) http.HandleFunc("/reindex", handlerReindex) http.HandleFunc("/random", handlerRandom) @@ -159,8 +134,6 @@ func main() { http.ServeFile(w, rq, WikiDir+"/static/favicon.ico") }) http.HandleFunc("/static/common.css", handlerStyle) - http.HandleFunc("/login", handlerLogin) - http.HandleFunc("/login-data", handlerLoginData) http.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) { http.Redirect(w, rq, "/page/"+util.HomePage, http.StatusSeeOther) }) diff --git a/templates/login.qtpl b/templates/auth.qtpl similarity index 78% rename from templates/login.qtpl rename to templates/auth.qtpl index 01baf88..bbb7d25 100644 --- a/templates/login.qtpl +++ b/templates/auth.qtpl @@ -43,3 +43,18 @@ {% endfunc %} +{% func LogoutHTML(can bool) %} +
+
+ {% if can %} +

Log out?

+

Confirm

+

Cancel

+ {% else %} +

You cannot log out because you are not logged in.

+

Login

+

← Home

+ {% endif %} +
+
+{% endfunc %} diff --git a/templates/auth.qtpl.go b/templates/auth.qtpl.go new file mode 100644 index 0000000..27d13fe --- /dev/null +++ b/templates/auth.qtpl.go @@ -0,0 +1,218 @@ +// Code generated by qtc from "auth.qtpl". DO NOT EDIT. +// See https://github.com/valyala/quicktemplate for details. + +//line templates/auth.qtpl:1 +package templates + +//line templates/auth.qtpl:1 +import "github.com/bouncepaw/mycorrhiza/user" + +//line templates/auth.qtpl:3 +import ( + qtio422016 "io" + + qt422016 "github.com/valyala/quicktemplate" +) + +//line templates/auth.qtpl:3 +var ( + _ = qtio422016.Copy + _ = qt422016.AcquireByteBuffer +) + +//line templates/auth.qtpl:3 +func StreamLoginHTML(qw422016 *qt422016.Writer) { +//line templates/auth.qtpl:3 + qw422016.N().S(` +
+
+ `) +//line templates/auth.qtpl:6 + if user.AuthUsed { +//line templates/auth.qtpl:6 + qw422016.N().S(` +

Login

+
+

Use the data you were given by the administrator.

+
+ Username + +
+
+ Password + +
+

By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you.

+ + Cancel +
+ `) +//line templates/auth.qtpl:22 + } else { +//line templates/auth.qtpl:22 + qw422016.N().S(` +

Administrator of this wiki have not configured any authorization method. You can make edits anonymously.

+

← Go home

+ `) +//line templates/auth.qtpl:25 + } +//line templates/auth.qtpl:25 + qw422016.N().S(` +
+
+`) +//line templates/auth.qtpl:28 +} + +//line templates/auth.qtpl:28 +func WriteLoginHTML(qq422016 qtio422016.Writer) { +//line templates/auth.qtpl:28 + qw422016 := qt422016.AcquireWriter(qq422016) +//line templates/auth.qtpl:28 + StreamLoginHTML(qw422016) +//line templates/auth.qtpl:28 + qt422016.ReleaseWriter(qw422016) +//line templates/auth.qtpl:28 +} + +//line templates/auth.qtpl:28 +func LoginHTML() string { +//line templates/auth.qtpl:28 + qb422016 := qt422016.AcquireByteBuffer() +//line templates/auth.qtpl:28 + WriteLoginHTML(qb422016) +//line templates/auth.qtpl:28 + qs422016 := string(qb422016.B) +//line templates/auth.qtpl:28 + qt422016.ReleaseByteBuffer(qb422016) +//line templates/auth.qtpl:28 + return qs422016 +//line templates/auth.qtpl:28 +} + +//line templates/auth.qtpl:30 +func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) { +//line templates/auth.qtpl:30 + qw422016.N().S(` +
+
+ `) +//line templates/auth.qtpl:33 + switch err { +//line templates/auth.qtpl:34 + case "unknown username": +//line templates/auth.qtpl:34 + qw422016.N().S(` +

Unknown username.

+ `) +//line templates/auth.qtpl:36 + case "wrong password": +//line templates/auth.qtpl:36 + qw422016.N().S(` +

Wrong password.

+ `) +//line templates/auth.qtpl:38 + default: +//line templates/auth.qtpl:38 + qw422016.N().S(` +

`) +//line templates/auth.qtpl:39 + qw422016.E().S(err) +//line templates/auth.qtpl:39 + qw422016.N().S(`

+ `) +//line templates/auth.qtpl:40 + } +//line templates/auth.qtpl:40 + qw422016.N().S(` +

← Try again

+
+
+`) +//line templates/auth.qtpl:44 +} + +//line templates/auth.qtpl:44 +func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) { +//line templates/auth.qtpl:44 + qw422016 := qt422016.AcquireWriter(qq422016) +//line templates/auth.qtpl:44 + StreamLoginErrorHTML(qw422016, err) +//line templates/auth.qtpl:44 + qt422016.ReleaseWriter(qw422016) +//line templates/auth.qtpl:44 +} + +//line templates/auth.qtpl:44 +func LoginErrorHTML(err string) string { +//line templates/auth.qtpl:44 + qb422016 := qt422016.AcquireByteBuffer() +//line templates/auth.qtpl:44 + WriteLoginErrorHTML(qb422016, err) +//line templates/auth.qtpl:44 + qs422016 := string(qb422016.B) +//line templates/auth.qtpl:44 + qt422016.ReleaseByteBuffer(qb422016) +//line templates/auth.qtpl:44 + return qs422016 +//line templates/auth.qtpl:44 +} + +//line templates/auth.qtpl:46 +func StreamLogoutHTML(qw422016 *qt422016.Writer, can bool) { +//line templates/auth.qtpl:46 + qw422016.N().S(` +
+
+ `) +//line templates/auth.qtpl:49 + if can { +//line templates/auth.qtpl:49 + qw422016.N().S(` +

Log out?

+

Confirm

+

Cancel

+ `) +//line templates/auth.qtpl:53 + } else { +//line templates/auth.qtpl:53 + qw422016.N().S(` +

You cannot log out because you are not logged in.

+

Login

+

← Home

+ `) +//line templates/auth.qtpl:57 + } +//line templates/auth.qtpl:57 + qw422016.N().S(` +
+
+`) +//line templates/auth.qtpl:60 +} + +//line templates/auth.qtpl:60 +func WriteLogoutHTML(qq422016 qtio422016.Writer, can bool) { +//line templates/auth.qtpl:60 + qw422016 := qt422016.AcquireWriter(qq422016) +//line templates/auth.qtpl:60 + StreamLogoutHTML(qw422016, can) +//line templates/auth.qtpl:60 + qt422016.ReleaseWriter(qw422016) +//line templates/auth.qtpl:60 +} + +//line templates/auth.qtpl:60 +func LogoutHTML(can bool) string { +//line templates/auth.qtpl:60 + qb422016 := qt422016.AcquireByteBuffer() +//line templates/auth.qtpl:60 + WriteLogoutHTML(qb422016, can) +//line templates/auth.qtpl:60 + qs422016 := string(qb422016.B) +//line templates/auth.qtpl:60 + qt422016.ReleaseByteBuffer(qb422016) +//line templates/auth.qtpl:60 + return qs422016 +//line templates/auth.qtpl:60 +} diff --git a/templates/login.qtpl.go b/templates/login.qtpl.go deleted file mode 100644 index fa23246..0000000 --- a/templates/login.qtpl.go +++ /dev/null @@ -1,159 +0,0 @@ -// Code generated by qtc from "login.qtpl". DO NOT EDIT. -// See https://github.com/valyala/quicktemplate for details. - -//line templates/login.qtpl:1 -package templates - -//line templates/login.qtpl:1 -import "github.com/bouncepaw/mycorrhiza/user" - -//line templates/login.qtpl:3 -import ( - qtio422016 "io" - - qt422016 "github.com/valyala/quicktemplate" -) - -//line templates/login.qtpl:3 -var ( - _ = qtio422016.Copy - _ = qt422016.AcquireByteBuffer -) - -//line templates/login.qtpl:3 -func StreamLoginHTML(qw422016 *qt422016.Writer) { -//line templates/login.qtpl:3 - qw422016.N().S(` -
-
- `) -//line templates/login.qtpl:6 - if user.AuthUsed { -//line templates/login.qtpl:6 - qw422016.N().S(` -

Login

-
-

Use the data you were given by the administrator.

-
- Username - -
-
- Password - -
-

By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you.

- - Cancel -
- `) -//line templates/login.qtpl:22 - } else { -//line templates/login.qtpl:22 - qw422016.N().S(` -

Administrator of this wiki have not configured any authorization method. You can make edits anonymously.

-

← Go home

- `) -//line templates/login.qtpl:25 - } -//line templates/login.qtpl:25 - qw422016.N().S(` -
-
-`) -//line templates/login.qtpl:28 -} - -//line templates/login.qtpl:28 -func WriteLoginHTML(qq422016 qtio422016.Writer) { -//line templates/login.qtpl:28 - qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/login.qtpl:28 - StreamLoginHTML(qw422016) -//line templates/login.qtpl:28 - qt422016.ReleaseWriter(qw422016) -//line templates/login.qtpl:28 -} - -//line templates/login.qtpl:28 -func LoginHTML() string { -//line templates/login.qtpl:28 - qb422016 := qt422016.AcquireByteBuffer() -//line templates/login.qtpl:28 - WriteLoginHTML(qb422016) -//line templates/login.qtpl:28 - qs422016 := string(qb422016.B) -//line templates/login.qtpl:28 - qt422016.ReleaseByteBuffer(qb422016) -//line templates/login.qtpl:28 - return qs422016 -//line templates/login.qtpl:28 -} - -//line templates/login.qtpl:30 -func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) { -//line templates/login.qtpl:30 - qw422016.N().S(` -
-
- `) -//line templates/login.qtpl:33 - switch err { -//line templates/login.qtpl:34 - case "unknown username": -//line templates/login.qtpl:34 - qw422016.N().S(` -

Unknown username.

- `) -//line templates/login.qtpl:36 - case "wrong password": -//line templates/login.qtpl:36 - qw422016.N().S(` -

Wrong password.

- `) -//line templates/login.qtpl:38 - default: -//line templates/login.qtpl:38 - qw422016.N().S(` -

`) -//line templates/login.qtpl:39 - qw422016.E().S(err) -//line templates/login.qtpl:39 - qw422016.N().S(`

- `) -//line templates/login.qtpl:40 - } -//line templates/login.qtpl:40 - qw422016.N().S(` -

← Try again

-
-
-`) -//line templates/login.qtpl:44 -} - -//line templates/login.qtpl:44 -func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) { -//line templates/login.qtpl:44 - qw422016 := qt422016.AcquireWriter(qq422016) -//line templates/login.qtpl:44 - StreamLoginErrorHTML(qw422016, err) -//line templates/login.qtpl:44 - qt422016.ReleaseWriter(qw422016) -//line templates/login.qtpl:44 -} - -//line templates/login.qtpl:44 -func LoginErrorHTML(err string) string { -//line templates/login.qtpl:44 - qb422016 := qt422016.AcquireByteBuffer() -//line templates/login.qtpl:44 - WriteLoginErrorHTML(qb422016, err) -//line templates/login.qtpl:44 - qs422016 := string(qb422016.B) -//line templates/login.qtpl:44 - qt422016.ReleaseByteBuffer(qb422016) -//line templates/login.qtpl:44 - return qs422016 -//line templates/login.qtpl:44 -} diff --git a/user/user.go b/user/user.go index cde4eca..b69268d 100644 --- a/user/user.go +++ b/user/user.go @@ -10,6 +10,29 @@ import ( "github.com/bouncepaw/mycorrhiza/util" ) +func LogoutFromRequest(w http.ResponseWriter, rq *http.Request) { + cookieFromUser, err := rq.Cookie("mycorrhiza_token") + if err == nil { + http.SetCookie(w, cookie("token", "", time.Unix(0, 0))) + terminateSession(cookieFromUser.Value) + } +} + +func (us *FixedUserStorage) userByToken(token string) *User { + if user, ok := us.Tokens[token]; ok { + return user + } + return nil +} + +func FromRequest(rq *http.Request) *User { + cookie, err := rq.Cookie("mycorrhiza_token") + if err != nil { + return nil + } + return UserStorage.userByToken(cookie.Value) +} + func LoginDataHTTP(w http.ResponseWriter, rq *http.Request, username, password string) string { w.Header().Set("Content-Type", "text/html;charset=utf-8") if !HasUsername(username) { @@ -36,12 +59,20 @@ func LoginDataHTTP(w http.ResponseWriter, rq *http.Request, username, password s func AddSession(username string) (string, error) { token, err := util.RandomString(16) if err == nil { - UserStorage.Tokens[token] = username + for _, user := range UserStorage.Users { + if user.Name == username { + UserStorage.Tokens[token] = user + } + } log.Println("New token for", username, "is", token) } return token, err } +func terminateSession(token string) { + delete(UserStorage.Tokens, token) +} + func HasUsername(username string) bool { for _, user := range UserStorage.Users { if user.Name == username { @@ -62,10 +93,10 @@ func CredentialsOK(username, password string) bool { type FixedUserStorage struct { Users []*User - Tokens map[string]string + Tokens map[string]*User } -var UserStorage = FixedUserStorage{Tokens: make(map[string]string)} +var UserStorage = FixedUserStorage{Tokens: make(map[string]*User)} func PopulateFixedUserStorage() { contents, err := ioutil.ReadFile(util.FixedCredentialsPath)