amuse.git

commit 6d3afa3ce45cf3e4c5efbd6a6e03002335b7581d

Author: Adam <git@apiote.tk>

log out

 db/db.go | 19 ++++++++++++++++
 front/capnproto.go | 5 ++++
 front/html.go | 5 ++++
 front/renderer.go | 1 
 libamuse/account.go | 26 +++++++++++++++++++++++
 libamuse/login.go | 21 ++++++++++++++++++
 router.go | 47 ++++++++++++++++++++++++++++++++++++++++++
 templates/loggedout.html | 29 +++++++++++++++++++++++++


diff --git a/db/db.go b/db/db.go
index 22a9ec1f6e0304f4a810ec57844f4097b4bad525..56174347af8520d35560cd2937cb5534ee90c5b1 100644
--- a/db/db.go
+++ b/db/db.go
@@ -241,6 +241,25 @@
 	return nil
 }
 
+func RemoveSession(username, token string) error {
+	db, err := sql.Open("sqlite3", utils.DataHome+"/amuse.db")
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "DB open err\n")
+		return err
+	}
+	defer db.Close()
+
+	rows, err := db.Exec(`delete from sessions where id = ? and username = ?`, token, username)
+	affected, _ := rows.RowsAffected()
+	if affected == 0 {
+		return EmptyError{
+			message: "No session " + token + " for user " + username,
+		}
+	}
+
+	return err
+}
+
 func GetItemExperiences(username, itemId string, itemType datastructure.ItemType) (map[string][]time.Time, error) {
 	times := map[string][]time.Time{}
 	user, err := GetUser(username)




diff --git a/front/capnproto.go b/front/capnproto.go
index 415ecbf730ac465a0c72e7c14d82a450eb7bc80e..d722f33eac2fc1462da7af45894dc824575f6699 100644
--- a/front/capnproto.go
+++ b/front/capnproto.go
@@ -56,6 +56,11 @@ 	// todo throw Wrong Accept
 	return TODO("implement CapnprotoRenderer.RenderLogin").(string)
 }
 
+func (CapnprotoRenderer) RenderLoggedOut(languages []language.Tag) string {
+	// todo throw Wrong Accept
+	return TODO("implement CapnprotoRenderer.RenderLogin").(string)
+}
+
 func (CapnprotoRenderer) RenderSignup(languages []language.Tag, err error, otp *otp.Key, sfaEnabled bool, username, qr string) string {
 	// todo throw Wrong Accept
 	return TODO("implement CapnprotoRenderer.RenderSignup").(string)




diff --git a/front/html.go b/front/html.go
index 4b7cf47308ddc67eacd01e9929435272c3bd04a5..6a97673d243210f588454ba3391f2ee228c738bc 100644
--- a/front/html.go
+++ b/front/html.go
@@ -140,6 +140,11 @@ 	data.State.Error = authError
 	return render(languages, data, "login")
 }
 
+func (HtmlRenderer) RenderLoggedOut(languages []language.Tag) string {
+	data := RenderData{}
+	return render(languages, data, "loggedout")
+}
+
 func (HtmlRenderer) RenderSignup(languages []language.Tag, authError error, key *otp.Key, sfaEnabled bool, username, qr string) string {
 	secret := struct {
 		Secret     string




diff --git a/front/renderer.go b/front/renderer.go
index 92917be7e499e9a6b7326f3ad6063c76b8377491..df1c27130a500d7744ebdd5daa4a9b1161b7c02a 100644
--- a/front/renderer.go
+++ b/front/renderer.go
@@ -30,6 +30,7 @@ 	RenderBookSerie(wikidata.BookSerie, []language.Tag) string
 	RenderAbout([]language.Tag) string
 	RenderErrorPage(int, []language.Tag) string
 	RenderLogin([]language.Tag, error, string) string
+	RenderLoggedOut([]language.Tag) string
 	RenderSignup([]language.Tag, error, *otp.Key, bool, string, string) string
 	RenderSignedup([]language.Tag, []string) string
 	RenderWatchlist(datastructure.Watchlist, []language.Tag) string




diff --git a/libamuse/account.go b/libamuse/account.go
index 03f52cefa49814df616661913dceacd14757fc7e..bca7dc747063ebecbb74cb9d08878a2262e7ad44 100644
--- a/libamuse/account.go
+++ b/libamuse/account.go
@@ -210,3 +210,29 @@ 	}
 
 	return err
 }
+
+func removeSession(args ...interface{}) (interface{}, error) {
+	data := args[0].(*RequestData)
+	var token string
+	if data.id == "0" {
+		token = data.auth.Token
+	} else {
+		token = data.id
+	}
+	username := data.username
+	err := db.RemoveSession(username, token)
+	return gott.Tuple(args), err
+}
+
+func SessionDelete(username string, auth accounts.Authentication, session, languages, mimetype string) error {
+	auth.Necessary = true
+	_, err := gott.
+		NewResult(gott.Tuple{&RequestData{id: session, language: languages, mimetype: mimetype, auth: auth, username: username}, &Result{}}).
+		Bind(parseLanguage).
+		Bind(verifyToken).
+		Bind(verifyUser).
+		Bind(removeSession).
+		Finish()
+
+	return err
+}




diff --git a/libamuse/login.go b/libamuse/login.go
index 222242f4441c45a587e6ce0b3abab81ab993bda6..b91dbee3f273376b5cb48319c4685a5654ce6019 100644
--- a/libamuse/login.go
+++ b/libamuse/login.go
@@ -35,3 +35,24 @@
 func DoLogin(username, password, sfa string, remember bool) (string, error) {
 	return accounts.Login(username, password, sfa, remember)
 }
+
+func renderLoggedOut(args ...interface{}) interface{} {
+	result := args[1].(*Result)
+	result.page = result.renderer.RenderLoggedOut(result.languages)
+	return gott.Tuple(args)
+}
+
+func ShowLoggedOut(languages, mimetype string) (string, error) {
+	r, err := gott.
+		NewResult(gott.Tuple{&RequestData{language: languages, mimetype: mimetype}, &Result{}}).
+		Bind(parseLanguage).
+		Bind(createRenderer).
+		Map(renderLoggedOut).
+		Finish()
+
+	if err != nil {
+		return "", err
+	} else {
+		return r.(gott.Tuple)[1].(*Result).page, nil
+	}
+}




diff --git a/router.go b/router.go
index 29955d0204f2aadcb6af8e609fd151e5f61ce8b9..6c68a99f2635d2b11fde5c0cc3289e980c17d26e 100644
--- a/router.go
+++ b/router.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	"notabug.org/apiote/amuse/accounts"
+	"notabug.org/apiote/amuse/db"
 	"notabug.org/apiote/amuse/front"
 	"notabug.org/apiote/amuse/libamuse"
 	"notabug.org/apiote/amuse/network"
@@ -431,6 +432,36 @@ 		}
 	}
 }
 
+func sessionDelete(w http.ResponseWriter, r *http.Request, username string, auth accounts.Authentication, session, acceptLanguages, mimetype string) {
+	err := libamuse.SessionDelete(username, auth, session, acceptLanguages, mimetype)
+	if err != nil {
+		render("", err, w, acceptLanguages, mimetype)
+	} else {
+		w.Header().Add("Location", "/loggedout")
+		w.WriteHeader(303)
+	}
+}
+
+func userSessions(w http.ResponseWriter, r *http.Request, username string, auth accounts.Authentication, acceptLanguages, mimetype string) {
+	path := strings.Split(r.URL.Path[1:], "/")
+	if len(path) == 3 {
+		// todo show sessions
+		renderError(404, w, nil, acceptLanguages, mimetype)
+	} else if len(path) == 4 {
+		if r.Method == "POST" {
+			r.ParseForm()
+			method := r.PostForm.Get("method")
+			session := path[3]
+			if method == "DELETE" {
+				sessionDelete(w, r, username, auth, session, acceptLanguages, mimetype)
+			}
+		} else if r.Method == "DELETE" {
+			session := path[3]
+			sessionDelete(w, r, username, auth, session, acceptLanguages, mimetype)
+		}
+	}
+}
+
 func userRouter(w http.ResponseWriter, r *http.Request) {
 	path := strings.Split(r.URL.Path[1:], "/")
 	acceptLanguages := r.Header.Get("Accept-Language")
@@ -456,12 +487,25 @@ 		case "tvqueue":
 			userTvQueue(w, r, username, auth, acceptLanguages, mimetype)
 		case "experiences":
 			userExperiences(w, r, username, auth, acceptLanguages, mimetype)
+		case "sessions":
+			userSessions(w, r, username, auth, acceptLanguages, mimetype)
 		default:
 			renderError(404, w, nil, acceptLanguages, mimetype)
 		}
 	}
 }
 
+func loggedout(w http.ResponseWriter, r *http.Request) {
+	acceptLanguages := r.Header.Get("Accept-Language")
+	mimetype := strings.Split(r.Header.Get("Accept"), ",")[0]
+
+	defer recovery(acceptLanguages, mimetype, w)
+
+	loggedout, err := libamuse.ShowLoggedOut(acceptLanguages, mimetype)
+	setAuthCookie(false, "", w)
+	render(loggedout, err, w, acceptLanguages, mimetype)
+}
+
 func route(port int) {
 	portStr := fmt.Sprintf(":%d", port)
 
@@ -479,6 +523,7 @@
 	http.HandleFunc("/login", login)
 	http.HandleFunc("/signup", signup)
 	http.HandleFunc("/signedup", signedup)
+	http.HandleFunc("/loggedout", loggedout)
 	fmt.Printf("running on %s\n", portStr)
 	e := http.ListenAndServe(portStr, nil)
 	if e != nil {
@@ -537,6 +582,8 @@ 		if _, ok := e.(front.NoSuchRendererError); ok {
 			renderError(406, w, e, languages, mimetype)
 		} else if httpError, ok := e.(network.HttpError); ok {
 			renderError(httpError.Status, w, httpError, languages, mimetype)
+		} else if _, ok := e.(db.EmptyError); ok {
+			renderError(410, w, e, languages, mimetype)
 		} else if authError, ok := e.(accounts.AuthError); ok {
 			if authError.Err.Error() == "401" {
 				w.Header().Add("WWW-Authenticate", "Bearer")




diff --git a/templates/loggedout.html b/templates/loggedout.html
new file mode 100644
index 0000000000000000000000000000000000000000..082441b48a14ef9354e917d1c15d57f9cd4b1456
--- /dev/null
+++ b/templates/loggedout.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="UTF-8">
+		<meta name="viewport" content="width=device-width, initial-scale=1.0">
+		<title>a·muse</title>
+		<link rel="stylesheet" href="/static/style/style.css" />
+		<link rel="icon" type="image/svg+xml" href="/static/img/logo.svg">
+		<link rel="apple-touch-icon" type="image/svg+xml" href="/static/img/logo.svg">
+	</head>
+	<body class="flex flex-column height-all">
+		<header class="w12 padding-bottom-_25 flex flex-row flex-justify-space flex-align-centre flex-content">
+			<a href="/" class="decoration-none">
+				<h1 class="inline valign-mid text sans margin-lr-1">a·muse</h1>
+			</a>
+		</header>
+		<main class="margin-lr-1 flex-fill">
+			<div class="flex flex-column height-fill flex-centre">
+				<div class="w12 flex flex-centre border-box left">
+					<div>
+						<div class="sans italic centre">„Mischief managed”</div>
+						<hr/>
+						<p class="sans">See You next time…</p>
+					</div>
+				</div>
+			</div>
+		</main>
+	</body>
+</html>