everywhere: use different session cookie names

If the user is hosting the dashboard in the same domain as
their blog (with a nginx suburi, for example), the two session
cookies clash; logging into one service logs you out of the other.
With this patch, both have separate names.

Fixes https://gitlab.com/commento/commento-ce/issues/49
This commit is contained in:
Adhityaa 2018-06-20 08:59:55 +05:30
parent 76a286d884
commit ef0f45527a
45 changed files with 189 additions and 282 deletions

View File

@ -26,7 +26,7 @@ func commentApprove(commentHex string) error {
func commentApproveHandler(w http.ResponseWriter, r *http.Request) { func commentApproveHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` CommenterToken *string `json:"commenterToken"`
CommentHex *string `json:"commentHex"` CommentHex *string `json:"commentHex"`
} }
@ -36,7 +36,7 @@ func commentApproveHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
c, err := commenterGetBySession(*x.Session) c, err := commenterGetByCommenterToken(*x.CommenterToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -25,7 +25,7 @@ func commentDelete(commentHex string) error {
func commentDeleteHandler(w http.ResponseWriter, r *http.Request) { func commentDeleteHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` CommenterToken *string `json:"commenterToken"`
CommentHex *string `json:"commentHex"` CommentHex *string `json:"commentHex"`
} }
@ -35,7 +35,7 @@ func commentDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
c, err := commenterGetBySession(*x.Session) c, err := commenterGetByCommenterToken(*x.CommenterToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -91,7 +91,7 @@ func commentList(commenterHex string, domain string, path string, includeUnappro
func commentListHandler(w http.ResponseWriter, r *http.Request) { func commentListHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` CommenterToken *string `json:"CommenterToken"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
Path *string `json:"path"` Path *string `json:"path"`
} }
@ -113,10 +113,10 @@ func commentListHandler(w http.ResponseWriter, r *http.Request) {
commenterHex := "anonymous" commenterHex := "anonymous"
isModerator := false isModerator := false
if *x.Session != "anonymous" { if *x.CommenterToken != "anonymous" {
c, err := commenterGetBySession(*x.Session) c, err := commenterGetByCommenterToken(*x.CommenterToken)
if err != nil { if err != nil {
if err == errorNoSuchSession { if err == errorNoSuchToken {
commenterHex = "anonymous" commenterHex = "anonymous"
} else { } else {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})

View File

@ -36,7 +36,7 @@ func commentNew(commenterHex string, domain string, path string, parentHex strin
func commentNewHandler(w http.ResponseWriter, r *http.Request) { func commentNewHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` CommenterToken *string `json:"commenterToken"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
Path *string `json:"path"` Path *string `json:"path"`
ParentHex *string `json:"parentHex"` ParentHex *string `json:"parentHex"`
@ -74,11 +74,11 @@ func commentNewHandler(w http.ResponseWriter, r *http.Request) {
var commenterHex string var commenterHex string
var state string var state string
if *x.Session == "anonymous" { if *x.CommenterToken == "anonymous" {
state = "unapproved" state = "unapproved"
commenterHex = "anonymous" commenterHex = "anonymous"
} else { } else {
c, err := commenterGetBySession(*x.Session) c, err := commenterGetByCommenterToken(*x.CommenterToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -45,7 +45,7 @@ func commentVote(commenterHex string, commentHex string, direction int) error {
func commentVoteHandler(w http.ResponseWriter, r *http.Request) { func commentVoteHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` CommenterToken *string `json:"commenterToken"`
CommentHex *string `json:"commentHex"` CommentHex *string `json:"commentHex"`
Direction *int `json:"direction"` Direction *int `json:"direction"`
} }
@ -56,12 +56,12 @@ func commentVoteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
if *x.Session == "anonymous" { if *x.CommenterToken == "anonymous" {
writeBody(w, response{"success": false, "message": errorUnauthorisedVote.Error()}) writeBody(w, response{"success": false, "message": errorUnauthorisedVote.Error()})
return return
} }
c, err := commenterGetBySession(*x.Session) c, err := commenterGetByCommenterToken(*x.CommenterToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -44,26 +44,26 @@ func commenterGetByEmail(provider string, email string) (commenter, error) {
return c, nil return c, nil
} }
func commenterGetBySession(session string) (commenter, error) { func commenterGetByCommenterToken(commenterToken string) (commenter, error) {
if session == "" { if commenterToken == "" {
return commenter{}, errorMissingField return commenter{}, errorMissingField
} }
statement := ` statement := `
SELECT commenterHex SELECT commenterHex
FROM commenterSessions FROM commenterSessions
WHERE session = $1; WHERE commenterToken = $1;
` `
row := db.QueryRow(statement, session) row := db.QueryRow(statement, commenterToken)
var commenterHex string var commenterHex string
if err := row.Scan(&commenterHex); err != nil { if err := row.Scan(&commenterHex); err != nil {
// TODO: is the only error? // TODO: is the only error?
return commenter{}, errorNoSuchSession return commenter{}, errorNoSuchToken
} }
if commenterHex == "none" { if commenterHex == "none" {
return commenter{}, errorNoSuchSession return commenter{}, errorNoSuchToken
} }
return commenterGetByHex(commenterHex) return commenterGetByHex(commenterHex)

View File

@ -30,16 +30,16 @@ func TestCommenterGetByHexEmpty(t *testing.T) {
} }
} }
func TestCommenterGetBySession(t *testing.T) { func TestCommenterGetByCommenterToken(t *testing.T) {
failTestOnError(t, setupTestEnv()) failTestOnError(t, setupTestEnv())
commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "") commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
session, _ := commenterSessionNew() commenterToken, _ := commenterTokenNew()
commenterSessionUpdate(session, commenterHex) commenterSessionUpdate(commenterToken, commenterHex)
c, err := commenterGetBySession(session) c, err := commenterGetByCommenterToken(commenterToken)
if err != nil { if err != nil {
t.Errorf("unexpected error getting commenter by hex: %v", err) t.Errorf("unexpected error getting commenter by hex: %v", err)
return return
@ -51,11 +51,11 @@ func TestCommenterGetBySession(t *testing.T) {
} }
} }
func TestCommenterGetBySessionEmpty(t *testing.T) { func TestCommenterGetByCommenterTokenEmpty(t *testing.T) {
failTestOnError(t, setupTestEnv()) failTestOnError(t, setupTestEnv())
if _, err := commenterGetBySession(""); err == nil { if _, err := commenterGetByCommenterToken(""); err == nil {
t.Errorf("expected error not found getting commenter with empty session") t.Errorf("expected error not found getting commenter with empty commenterToken")
return return
} }
} }
@ -65,9 +65,9 @@ func TestCommenterGetByName(t *testing.T) {
commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "") commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
session, _ := commenterSessionNew() commenterToken, _ := commenterTokenNew()
commenterSessionUpdate(session, commenterHex) commenterSessionUpdate(commenterToken, commenterHex)
c, err := commenterGetByEmail("google", "test@example.com") c, err := commenterGetByEmail("google", "test@example.com")
if err != nil { if err != nil {

View File

@ -29,24 +29,24 @@ func commenterLogin(email string, password string) (string, error) {
return "", errorInvalidEmailPassword return "", errorInvalidEmailPassword
} }
session, err := randomHex(32) commenterToken, err := randomHex(32)
if err != nil { if err != nil {
logger.Errorf("cannot create session hex: %v", err) logger.Errorf("cannot create commenterToken: %v", err)
return "", errorInternal return "", errorInternal
} }
statement = ` statement = `
INSERT INTO INSERT INTO
commenterSessions (session, commenterHex, creationDate) commenterSessions (commenterToken, commenterHex, creationDate)
VALUES ($1, $2, $3 ); VALUES ($1, $2, $3 );
` `
_, err = db.Exec(statement, session, commenterHex, time.Now().UTC()) _, err = db.Exec(statement, commenterToken, commenterHex, time.Now().UTC())
if err != nil { if err != nil {
logger.Errorf("cannot insert session token: %v\n", err) logger.Errorf("cannot insert commenterToken token: %v\n", err)
return "", errorInternal return "", errorInternal
} }
return session, nil return commenterToken, nil
} }
func commenterLoginHandler(w http.ResponseWriter, r *http.Request) { func commenterLoginHandler(w http.ResponseWriter, r *http.Request) {
@ -61,11 +61,11 @@ func commenterLoginHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
session, err := commenterLogin(*x.Email, *x.Password) commenterToken, err := commenterLogin(*x.Email, *x.Password)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return
} }
writeBody(w, response{"success": true, "session": session}) writeBody(w, response{"success": true, "commenterToken": commenterToken})
} }

View File

@ -24,8 +24,8 @@ func TestCommenterLoginBasics(t *testing.T) {
return return
} }
if session, err := commenterLogin("test@example.com", "hunter2"); session == "" { if commenterToken, err := commenterLogin("test@example.com", "hunter2"); commenterToken == "" {
t.Errorf("empty session on successful login: %v", err) t.Errorf("empty comenterToken on successful login: %v", err)
return return
} }
} }

View File

@ -6,7 +6,7 @@ import (
func commenterSelfHandler(w http.ResponseWriter, r *http.Request) { func commenterSelfHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` CommenterToken *string `json:"commenterToken"`
} }
var x request var x request
@ -15,7 +15,7 @@ func commenterSelfHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
c, err := commenterGetBySession(*x.Session) c, err := commenterGetByCommenterToken(*x.CommenterToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -4,8 +4,11 @@ import (
"time" "time"
) )
// A session is a 3-field entry of a token, a hex, and a creation date. Do
// not confuse session and token; the token is just an identifying string,
// while the session contains more information.
type commenterSession struct { type commenterSession struct {
Session string `json:"session"` CommenterToken string `json:"commenterToken"`
CommenterHex string `json:"commenterHex"` CommenterHex string `json:"commenterHex"`
CreationDate time.Time `json:"creationDate"` CreationDate time.Time `json:"creationDate"`
} }

View File

@ -1,25 +0,0 @@
package main
import ()
func commenterSessionGet(session string) (commenterSession, error) {
if session == "" {
return commenterSession{}, errorMissingField
}
statement := `
SELECT commenterHex, creationDate
FROM commenterSessions
WHERE session=$1;
`
row := db.QueryRow(statement, session)
cs := commenterSession{}
if err := row.Scan(&cs.CommenterHex, &cs.CreationDate); err != nil {
return commenterSession{}, errorNoSuchSession
}
cs.Session = session
return cs, nil
}

View File

@ -1,46 +0,0 @@
package main
import (
"testing"
)
func TestCommenterSessionGetBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "https://example.com/photo.jpg", "google", "")
session, _ := commenterSessionNew()
commenterSessionUpdate(session, commenterHex)
cs, err := commenterSessionGet(session)
if err != nil {
t.Errorf("unexpected error found when getting session information: %v", err)
return
}
if cs.CommenterHex != commenterHex {
t.Errorf("expected commenterHex=%s got commenterHex=%s", commenterHex, cs.CommenterHex)
return
}
}
func TestCommenterSessionGetDNE(t *testing.T) {
failTestOnError(t, setupTestEnv())
_, err := commenterSessionGet("does-not-exist")
if err == nil {
t.Errorf("expected error not found when invalid session")
return
}
}
func TestCommenterSessionGetEmpty(t *testing.T) {
failTestOnError(t, setupTestEnv())
_, err := commenterSessionGet("")
if err == nil {
t.Errorf("expected error not found with empty session")
return
}
}

View File

@ -5,33 +5,33 @@ import (
"time" "time"
) )
func commenterSessionNew() (string, error) { func commenterTokenNew() (string, error) {
session, err := randomHex(32) commenterToken, err := randomHex(32)
if err != nil { if err != nil {
logger.Errorf("cannot create session hex: %v", err) logger.Errorf("cannot create commenterToken: %v", err)
return "", errorInternal return "", errorInternal
} }
statement := ` statement := `
INSERT INTO INSERT INTO
commenterSessions (session, creationDate) commenterSessions (commenterToken, creationDate)
VALUES ($1, $2 ); VALUES ($1, $2 );
` `
_, err = db.Exec(statement, session, time.Now().UTC()) _, err = db.Exec(statement, commenterToken, time.Now().UTC())
if err != nil { if err != nil {
logger.Errorf("cannot insert new session: %v", err) logger.Errorf("cannot insert new commenterToken: %v", err)
return "", errorInternal return "", errorInternal
} }
return session, nil return commenterToken, nil
} }
func commenterSessionNewHandler(w http.ResponseWriter, r *http.Request) { func commenterTokenNewHandler(w http.ResponseWriter, r *http.Request) {
session, err := commenterSessionNew() commenterToken, err := commenterTokenNew()
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return
} }
writeBody(w, response{"success": true, "session": session}) writeBody(w, response{"success": true, "commenterToken": commenterToken})
} }

View File

@ -4,11 +4,11 @@ import (
"testing" "testing"
) )
func TestCommenterSessionNewBasics(t *testing.T) { func TestCommenterTokenNewBasics(t *testing.T) {
failTestOnError(t, setupTestEnv()) failTestOnError(t, setupTestEnv())
if _, err := commenterSessionNew(); err != nil { if _, err := commenterTokenNew(); err != nil {
t.Errorf("unexpected error creating new session: %v", err) t.Errorf("unexpected error creating new commenterToken: %v", err)
return return
} }
} }

View File

@ -2,19 +2,19 @@ package main
import () import ()
func commenterSessionUpdate(session string, commenterHex string) error { func commenterSessionUpdate(commenterToken string, commenterHex string) error {
if session == "" || commenterHex == "" { if commenterToken == "" || commenterHex == "" {
return errorMissingField return errorMissingField
} }
statement := ` statement := `
UPDATE commenterSessions UPDATE commenterSessions
SET commenterHex=$2 SET commenterHex = $2
WHERE session=$1; WHERE commenterToken = $1;
` `
_, err := db.Exec(statement, session, commenterHex) _, err := db.Exec(statement, commenterToken, commenterHex)
if err != nil { if err != nil {
logger.Errorf("error updating commenterHex in commenterSessions: %v", err) logger.Errorf("error updating commenterHex: %v", err)
return errorInternal return errorInternal
} }

View File

@ -7,10 +7,10 @@ import (
func TestCommenterSessionUpdateBasics(t *testing.T) { func TestCommenterSessionUpdateBasics(t *testing.T) {
failTestOnError(t, setupTestEnv()) failTestOnError(t, setupTestEnv())
session, _ := commenterSessionNew() commenterToken, _ := commenterTokenNew()
if err := commenterSessionUpdate(session, "temp-commenter-hex"); err != nil { if err := commenterSessionUpdate(commenterToken, "temp-commenter-hex"); err != nil {
t.Errorf("unexpected error updating session to commenterHex: %v", err) t.Errorf("unexpected error updating commenter session: %v", err)
return return
} }
} }
@ -19,7 +19,7 @@ func TestCommenterSessionUpdateEmpty(t *testing.T) {
failTestOnError(t, setupTestEnv()) failTestOnError(t, setupTestEnv())
if err := commenterSessionUpdate("", "temp-commenter-hex"); err == nil { if err := commenterSessionUpdate("", "temp-commenter-hex"); err == nil {
t.Errorf("expected error not found when updating with empty session") t.Errorf("expected error not found when updating with empty commenterToken")
return return
} }
} }

View File

@ -40,6 +40,8 @@ func performMigrationsFromDir(dir string) error {
filenames[filename] = true filenames[filename] = true
} }
logger.Infof("%d migrations already installed, looking for more", len(filenames))
completed := 0 completed := 0
for _, file := range files { for _, file := range files {
if strings.HasSuffix(file.Name(), ".sql") { if strings.HasSuffix(file.Name(), ".sql") {
@ -73,7 +75,9 @@ func performMigrationsFromDir(dir string) error {
} }
if completed > 0 { if completed > 0 {
logger.Infof("%d migrations found, %d new migrations completed (%d total)", len(filenames), completed, len(filenames)+completed) logger.Infof("%d new migrations completed (%d total)", completed, len(filenames)+completed)
} else {
logger.Infof("none found")
} }
return nil return nil

View File

@ -65,7 +65,7 @@ func domainDelete(domain string) error {
func domainDeleteHandler(w http.ResponseWriter, r *http.Request) { func domainDeleteHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
} }
@ -75,7 +75,7 @@ func domainDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -168,7 +168,7 @@ func domainImportDisqus(domain string, url string) (int, error) {
func domainImportDisqusHandler(w http.ResponseWriter, r *http.Request) { func domainImportDisqusHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
URL *string `json:"url"` URL *string `json:"url"`
} }
@ -179,7 +179,7 @@ func domainImportDisqusHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -42,7 +42,7 @@ func domainList(ownerHex string) ([]domain, error) {
func domainListHandler(w http.ResponseWriter, r *http.Request) { func domainListHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
} }
var x request var x request
@ -51,7 +51,7 @@ func domainListHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -24,7 +24,7 @@ func domainModeratorDelete(domain string, email string) error {
func domainModeratorDeleteHandler(w http.ResponseWriter, r *http.Request) { func domainModeratorDeleteHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
Email *string `json:"email"` Email *string `json:"email"`
} }
@ -35,7 +35,7 @@ func domainModeratorDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -26,7 +26,7 @@ func domainModeratorNew(domain string, email string) error {
func domainModeratorNewHandler(w http.ResponseWriter, r *http.Request) { func domainModeratorNewHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
Email *string `json:"email"` Email *string `json:"email"`
} }
@ -37,7 +37,7 @@ func domainModeratorNewHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -26,7 +26,7 @@ func domainNew(ownerHex string, name string, domain string) error {
func domainNewHandler(w http.ResponseWriter, r *http.Request) { func domainNewHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
Name *string `json:"name"` Name *string `json:"name"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
} }
@ -37,7 +37,7 @@ func domainNewHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -39,7 +39,7 @@ func domainStatistics(domain string) ([]int64, error) {
func domainStatisticsHandler(w http.ResponseWriter, r *http.Request) { func domainStatisticsHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
Domain *string `json:"domain"` Domain *string `json:"domain"`
} }
@ -49,7 +49,7 @@ func domainStatisticsHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -22,7 +22,7 @@ func domainUpdate(d domain) error {
func domainUpdateHandler(w http.ResponseWriter, r *http.Request) { func domainUpdateHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
D *domain `json:"domain"` D *domain `json:"domain"`
} }
@ -32,7 +32,7 @@ func domainUpdateHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
o, err := ownerGetBySession(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return

View File

@ -17,7 +17,7 @@ var errorNoSuchConfirmationToken = errors.New("This email confirmation link has
var errorNoSuchResetToken = errors.New("This password reset link has expired.") var errorNoSuchResetToken = errors.New("This password reset link has expired.")
var errorNotAuthorised = errors.New("You're not authorised to access that.") var errorNotAuthorised = errors.New("You're not authorised to access that.")
var errorEmailAlreadyExists = errors.New("That email address has already been registered.") var errorEmailAlreadyExists = errors.New("That email address has already been registered.")
var errorNoSuchSession = errors.New("No such session/state.") var errorNoSuchToken = errors.New("No such session token.")
var errorNoSuchCommenter = errors.New("No such commenter.") var errorNoSuchCommenter = errors.New("No such commenter.")
var errorAlreadyUpvoted = errors.New("You have already upvoted that comment.") var errorAlreadyUpvoted = errors.New("You have already upvoted that comment.")
var errorNoSuchDomain = errors.New("This domain is not registered with Commento.") var errorNoSuchDomain = errors.New("This domain is not registered with Commento.")
@ -32,8 +32,6 @@ var errorForbiddenEdit = errors.New("You cannot edit someone else's comment.")
var errorMissingSmtpAddress = errors.New("Missing SMTP_FROM_ADDRESS") var errorMissingSmtpAddress = errors.New("Missing SMTP_FROM_ADDRESS")
var errorSmtpNotConfigured = errors.New("SMTP is not configured.") var errorSmtpNotConfigured = errors.New("SMTP is not configured.")
var errorOauthMisconfigured = errors.New("OAuth is misconfigured.") var errorOauthMisconfigured = errors.New("OAuth is misconfigured.")
var errorUnassociatedSession = errors.New("No user associated with that session.")
var errorSessionAlreadyInUse = errors.New("Session is already in use.")
var errorCannotReadResponse = errors.New("Cannot read response.") var errorCannotReadResponse = errors.New("Cannot read response.")
var errorNotModerator = errors.New("You need to be a moderator to do that.") var errorNotModerator = errors.New("You need to be a moderator to do that.")
var errorNotADirectory = errors.New("The given path is not a directory.") var errorNotADirectory = errors.New("The given path is not a directory.")

View File

@ -9,11 +9,11 @@ import (
) )
func googleCallbackHandler(w http.ResponseWriter, r *http.Request) { func googleCallbackHandler(w http.ResponseWriter, r *http.Request) {
session := r.FormValue("state") commenterToken := r.FormValue("state")
code := r.FormValue("code") code := r.FormValue("code")
_, err := commenterSessionGet(session) _, err := commenterGetByCommenterToken(commenterToken)
if err != nil && err != errorNoSuchSession { if err != nil && err != errorNoSuchToken {
fmt.Fprintf(w, "Error: %s\n", err.Error()) fmt.Fprintf(w, "Error: %s\n", err.Error())
return return
} }
@ -73,7 +73,7 @@ func googleCallbackHandler(w http.ResponseWriter, r *http.Request) {
commenterHex = c.CommenterHex commenterHex = c.CommenterHex
} }
if err := commenterSessionUpdate(session, commenterHex); err != nil { if err := commenterSessionUpdate(commenterToken, commenterHex); err != nil {
fmt.Fprintf(w, "Error: %s", err.Error()) fmt.Fprintf(w, "Error: %s", err.Error())
return return
} }

View File

@ -12,14 +12,14 @@ func googleRedirectHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
session := r.FormValue("session") commenterToken := r.FormValue("commenterToken")
_, err := commenterGetBySession(session) _, err := commenterGetByCommenterToken(commenterToken)
if err != nil && err != errorNoSuchSession { if err != nil && err != errorNoSuchToken {
fmt.Fprintf(w, "error: %s\n", err.Error()) fmt.Fprintf(w, "error: %s\n", err.Error())
return return
} }
url := googleConfig.AuthCodeURL(session) url := googleConfig.AuthCodeURL(commenterToken)
http.Redirect(w, r, url, http.StatusFound) http.Redirect(w, r, url, http.StatusFound)
} }

View File

@ -23,8 +23,8 @@ func ownerGetByEmail(email string) (owner, error) {
return o, nil return o, nil
} }
func ownerGetBySession(session string) (owner, error) { func ownerGetByOwnerToken(ownerToken string) (owner, error) {
if session == "" { if ownerToken == "" {
return owner{}, errorMissingField return owner{}, errorMissingField
} }
@ -33,10 +33,10 @@ func ownerGetBySession(session string) (owner, error) {
FROM owners FROM owners
WHERE email IN ( WHERE email IN (
SELECT email FROM ownerSessions SELECT email FROM ownerSessions
WHERE session=$1 WHERE ownerToken = $1
); );
` `
row := db.QueryRow(statement, session) row := db.QueryRow(statement, ownerToken)
var o owner var o owner
if err := row.Scan(&o.OwnerHex, &o.Email, &o.Name, &o.ConfirmedEmail, &o.JoinDate); err != nil { if err := row.Scan(&o.OwnerHex, &o.Email, &o.Name, &o.ConfirmedEmail, &o.JoinDate); err != nil {

View File

@ -30,16 +30,16 @@ func TestOwnerGetByEmailDNE(t *testing.T) {
} }
} }
func TestOwnerGetBySessionBasics(t *testing.T) { func TestOwnerGetByOwnerTokenBasics(t *testing.T) {
failTestOnError(t, setupTestEnv()) failTestOnError(t, setupTestEnv())
ownerHex, _ := ownerNew("test@example.com", "Test", "hunter2") ownerHex, _ := ownerNew("test@example.com", "Test", "hunter2")
session, _ := ownerLogin("test@example.com", "hunter2") ownerToken, _ := ownerLogin("test@example.com", "hunter2")
o, err := ownerGetBySession(session) o, err := ownerGetByOwnerToken(ownerToken)
if err != nil { if err != nil {
t.Errorf("unexpected error on ownerGetBySession: %v", err) t.Errorf("unexpected error on ownerGetByOwnerToken: %v", err)
return return
} }
@ -49,11 +49,11 @@ func TestOwnerGetBySessionBasics(t *testing.T) {
} }
} }
func TestOwnerGetBySessionDNE(t *testing.T) { func TestOwnerGetByOwnerTokenDNE(t *testing.T) {
failTestOnError(t, setupTestEnv()) failTestOnError(t, setupTestEnv())
if _, err := ownerGetBySession("does-not-exist"); err == nil { if _, err := ownerGetByOwnerToken("does-not-exist"); err == nil {
t.Errorf("expected error not found on ownerGetBySession before creating an account") t.Errorf("expected error not found on ownerGetByOwnerToken before creating an account")
return return
} }
} }

View File

@ -34,24 +34,24 @@ func ownerLogin(email string, password string) (string, error) {
return "", errorInvalidEmailPassword return "", errorInvalidEmailPassword
} }
session, err := randomHex(32) ownerToken, err := randomHex(32)
if err != nil { if err != nil {
logger.Errorf("cannot create session hex: %v", err) logger.Errorf("cannot create ownerToken: %v", err)
return "", errorInternal return "", errorInternal
} }
statement = ` statement = `
INSERT INTO INSERT INTO
ownerSessions (session, ownerHex, loginDate) ownerSessions (ownerToken, ownerHex, loginDate)
VALUES ($1, $2, $3 ); VALUES ($1, $2, $3 );
` `
_, err = db.Exec(statement, session, ownerHex, time.Now().UTC()) _, err = db.Exec(statement, ownerToken, ownerHex, time.Now().UTC())
if err != nil { if err != nil {
logger.Errorf("cannot insert session token: %v\n", err) logger.Errorf("cannot insert ownerSession: %v\n", err)
return "", errorInternal return "", errorInternal
} }
return session, nil return ownerToken, nil
} }
func ownerLoginHandler(w http.ResponseWriter, r *http.Request) { func ownerLoginHandler(w http.ResponseWriter, r *http.Request) {
@ -66,11 +66,11 @@ func ownerLoginHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
session, err := ownerLogin(*x.Email, *x.Password) ownerToken, err := ownerLogin(*x.Email, *x.Password)
if err != nil { if err != nil {
writeBody(w, response{"success": false, "message": err.Error()}) writeBody(w, response{"success": false, "message": err.Error()})
return return
} }
writeBody(w, response{"success": true, "session": session}) writeBody(w, response{"success": true, "ownerToken": ownerToken})
} }

View File

@ -24,8 +24,8 @@ func TestOwnerLoginBasics(t *testing.T) {
return return
} }
if session, err := ownerLogin("test@example.com", "hunter2"); session == "" { if ownerToken, err := ownerLogin("test@example.com", "hunter2"); ownerToken == "" {
t.Errorf("empty session on successful login: %v", err) t.Errorf("empty token on successful login: %v", err)
return return
} }
} }

View File

@ -4,18 +4,9 @@ import (
"net/http" "net/http"
) )
func ownerSelf(session string) (bool, owner) {
o, err := ownerGetBySession(session)
if err != nil {
return false, owner{}
}
return true, o
}
func ownerSelfHandler(w http.ResponseWriter, r *http.Request) { func ownerSelfHandler(w http.ResponseWriter, r *http.Request) {
type request struct { type request struct {
Session *string `json:"session"` OwnerToken *string `json:"ownerToken"`
} }
var x request var x request
@ -24,7 +15,16 @@ func ownerSelfHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
loggedIn, o := ownerSelf(*x.Session) o, err := ownerGetByOwnerToken(*x.OwnerToken)
if err == errorNoSuchToken {
writeBody(w, response{"success": true, "loggedIn": false})
return
}
writeBody(w, response{"success": true, "loggedIn": loggedIn, "owner": o}) if err != nil {
writeBody(w, response{"success": false, "message": err.Error()})
return
}
writeBody(w, response{"success": true, "loggedIn": true, "owner": o})
} }

View File

@ -1,32 +0,0 @@
package main
import (
"testing"
)
func TestOwnerSelfBasics(t *testing.T) {
failTestOnError(t, setupTestEnv())
ownerNew("test@example.com", "Test", "hunter2")
session, _ := ownerLogin("test@example.com", "hunter2")
loggedIn, o := ownerSelf(session)
if !loggedIn {
t.Errorf("expected loggedIn=true got loggedIn=false")
return
}
if o.Name != "Test" {
t.Errorf("expected name=Test got name=%s", o.Name)
return
}
}
func TestOwnerSelfNotLoggedIn(t *testing.T) {
failTestOnError(t, setupTestEnv())
if loggedIn, _ := ownerSelf("does-not-exist"); loggedIn {
t.Errorf("expected loggedIn=false got loggedIn=true")
return
}
}

View File

@ -21,7 +21,7 @@ func initAPIRouter(router *mux.Router) error {
router.HandleFunc("/api/domain/statistics", domainStatisticsHandler).Methods("POST") router.HandleFunc("/api/domain/statistics", domainStatisticsHandler).Methods("POST")
router.HandleFunc("/api/domain/import/disqus", domainImportDisqusHandler).Methods("POST") router.HandleFunc("/api/domain/import/disqus", domainImportDisqusHandler).Methods("POST")
router.HandleFunc("/api/commenter/session/new", commenterSessionNewHandler).Methods("GET") router.HandleFunc("/api/commenter/token/new", commenterTokenNewHandler).Methods("GET")
router.HandleFunc("/api/commenter/new", commenterNewHandler).Methods("POST") router.HandleFunc("/api/commenter/new", commenterNewHandler).Methods("POST")
router.HandleFunc("/api/commenter/login", commenterLoginHandler).Methods("POST") router.HandleFunc("/api/commenter/login", commenterLoginHandler).Methods("POST")
router.HandleFunc("/api/commenter/self", commenterSelfHandler).Methods("POST") router.HandleFunc("/api/commenter/self", commenterSelfHandler).Methods("POST")

View File

@ -0,0 +1,5 @@
ALTER TABLE ownerSessions
RENAME COLUMN session TO ownerToken;
ALTER TABLE commenterSessions
RENAME COLUMN session TO commenterToken

View File

@ -192,36 +192,36 @@
} }
function sessionGet() { function commenterTokenGet() {
var session = cookieGet("session"); var commenterToken = cookieGet("commenterToken");
if (session === undefined) if (commenterToken === undefined)
return "anonymous"; return "anonymous";
return session; return commenterToken;
} }
global.logout = function() { global.logout = function() {
cookieSet("session", "anonymous"); cookieSet("commenterToken", "anonymous");
refreshAll(); refreshAll();
} }
function selfGet(callback) { function selfGet(callback) {
var session = sessionGet(); var commenterToken = commenterTokenGet();
if (session == "anonymous") { if (commenterToken == "anonymous") {
isAuthenticated = false; isAuthenticated = false;
call(callback); call(callback);
return; return;
} }
var json = { var json = {
session: sessionGet(), "commenterToken": commenterTokenGet(),
}; };
post(origin + "/api/commenter/self", json, function(resp) { post(origin + "/api/commenter/self", json, function(resp) {
if (!resp.success) { if (!resp.success) {
cookieSet("session", "anonymous"); cookieSet("commenterToken", "anonymous");
call(callback); call(callback);
return; return;
} }
@ -344,9 +344,9 @@
function commentsGet(callback) { function commentsGet(callback) {
var json = { var json = {
session: sessionGet(), "commenterToken": commenterTokenGet(),
domain: location.host, "domain": location.host,
path: location.pathname, "path": location.pathname,
}; };
post(origin + "/api/comment/list", json, function(resp) { post(origin + "/api/comment/list", json, function(resp) {
@ -487,7 +487,7 @@
} }
var json = { var json = {
"session": sessionGet(), "commenterToken": commenterTokenGet(),
"domain": location.host, "domain": location.host,
"path": location.pathname, "path": location.pathname,
"parentHex": id, "parentHex": id,
@ -773,7 +773,7 @@
global.commentApprove = function(commentHex) { global.commentApprove = function(commentHex) {
var json = { var json = {
"session": sessionGet(), "commenterToken": commenterTokenGet(),
"commentHex": commentHex, "commentHex": commentHex,
} }
@ -796,7 +796,7 @@
global.commentDelete = function(commentHex) { global.commentDelete = function(commentHex) {
var json = { var json = {
"session": sessionGet(), "commenterToken": commenterTokenGet(),
"commentHex": commentHex, "commentHex": commentHex,
} }
@ -826,7 +826,7 @@
var score = $(ID_SCORE + commentHex); var score = $(ID_SCORE + commentHex);
var json = { var json = {
"session": sessionGet(), "commenterToken": commenterTokenGet(),
"commentHex": commentHex, "commentHex": commentHex,
"direction": direction, "direction": direction,
}; };
@ -970,7 +970,7 @@
global.commentoAuth = function(provider) { global.commentoAuth = function(provider) {
if (provider == "anonymous") { if (provider == "anonymous") {
cookieSet("session", "anonymous"); cookieSet("commenterToken", "anonymous");
chosenAnonymous = true; chosenAnonymous = true;
refreshAll(); refreshAll();
return; return;
@ -978,15 +978,15 @@
var popup = window.open("", "_blank"); var popup = window.open("", "_blank");
get(origin + "/api/commenter/session/new", function(resp) { get(origin + "/api/commenter/token/new", function(resp) {
if (!resp.success) { if (!resp.success) {
errorShow(resp.message); errorShow(resp.message);
return; return;
} }
cookieSet("session", resp.session); cookieSet("commenterToken", resp.commenterToken);
popup.location = origin + "/api/oauth/" + provider + "/redirect?session=" + resp.session; popup.location = origin + "/api/oauth/" + provider + "/redirect?commenterToken=" + resp.commenterToken;
var interval = setInterval(function() { var interval = setInterval(function() {
if (popup.closed) { if (popup.closed) {
@ -1144,8 +1144,8 @@
function loginUP(username, password) { function loginUP(username, password) {
var json = { var json = {
email: username, "email": username,
password: password, "password": password,
}; };
post(origin + "/api/commenter/login", json, function(resp) { post(origin + "/api/commenter/login", json, function(resp) {
@ -1155,7 +1155,7 @@
return return
} }
cookieSet("session", resp.session); cookieSet("commenterToken", resp.commenterToken);
refreshAll(); refreshAll();
}); });
} }
@ -1176,10 +1176,10 @@
var password = $(ID_LOGIN_BOX_PASSWORD_INPUT); var password = $(ID_LOGIN_BOX_PASSWORD_INPUT);
var json = { var json = {
email: email.value, "email": email.value,
name: name.value, "name": name.value,
website: website.value, "website": website.value,
password: password.value, "password": password.value,
}; };
post(origin + "/api/commenter/new", json, function(resp) { post(origin + "/api/commenter/new", json, function(resp) {

View File

@ -36,9 +36,9 @@
// Creates a new domain. // Creates a new domain.
global.domainNewHandler = function() { global.domainNewHandler = function() {
var json = { var json = {
session: global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
name: $("#new-domain-name").val(), "name": $("#new-domain-name").val(),
domain: $("#new-domain-domain").val(), "domain": $("#new-domain-domain").val(),
} }
global.buttonDisable("#add-site-button"); global.buttonDisable("#add-site-button");
@ -66,7 +66,7 @@
// Refreshes the list of domains. // Refreshes the list of domains.
global.domainRefresh = function(callback) { global.domainRefresh = function(callback) {
var json = { var json = {
session: global.cookieGet("session"), ownerToken: global.cookieGet("ownerToken"),
}; };
global.post(global.commentoOrigin + "/api/domain/list", json, function(resp) { global.post(global.commentoOrigin + "/api/domain/list", json, function(resp) {
@ -107,8 +107,8 @@
// Updates a domain with the backend. // Updates a domain with the backend.
global.domainUpdate = function(domain, callback) { global.domainUpdate = function(domain, callback) {
var json = { var json = {
session: global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
domain: domain, "domain": domain,
}; };
global.post(global.commentoOrigin + "/api/domain/update", json, function(resp) { global.post(global.commentoOrigin + "/api/domain/update", json, function(resp) {
@ -126,8 +126,8 @@
// Deletes a domain. // Deletes a domain.
global.domainDelete = function(domain, callback) { global.domainDelete = function(domain, callback) {
var json = { var json = {
session: global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
domain: domain, "domain": domain,
}; };
global.post(global.commentoOrigin + "/api/domain/delete", json, function(resp) { global.post(global.commentoOrigin + "/api/domain/delete", json, function(resp) {

View File

@ -12,9 +12,9 @@
var data = global.dashboard.$data; var data = global.dashboard.$data;
var json = { var json = {
session: global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
domain: data.domains[data.cd].domain, "domain": data.domains[data.cd].domain,
url: url, "url": url,
} }
global.buttonDisable("#disqus-import-button"); global.buttonDisable("#disqus-import-button");

View File

@ -13,9 +13,9 @@
var email = $("#new-mod").val(); var email = $("#new-mod").val();
var json = { var json = {
session: global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
domain: data.domains[data.cd].domain, "domain": data.domains[data.cd].domain,
email: email, "email": email,
} }
var idx = -1; var idx = -1;
@ -53,9 +53,9 @@
var data = global.dashboard.$data; var data = global.dashboard.$data;
var json = { var json = {
session: global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
domain: data.domains[data.cd].domain, "domain": data.domains[data.cd].domain,
email: email, "email": email,
} }
var idx = -1; var idx = -1;

View File

@ -38,8 +38,8 @@
var data = global.dashboard.$data; var data = global.dashboard.$data;
var json = { var json = {
session: global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
domain: data.domains[data.cd].domain, "domain": data.domains[data.cd].domain,
} }
$(".view").hide(); $(".view").hide();

View File

@ -65,7 +65,7 @@
return; return;
} }
global.cookieSet("session", resp.session); global.cookieSet("ownerToken", resp.ownerToken);
document.location = "/dashboard"; document.location = "/dashboard";
}); });
}; };

View File

@ -1,7 +1,7 @@
(function (global, document) { (function (global, document) {
global.logout = function() { global.logout = function() {
global.cookieSet("session", ""); global.cookieSet("ownerToken", "");
document.location = "/login"; document.location = "/login";
} }

View File

@ -3,7 +3,7 @@
// Get self details. // Get self details.
global.selfGet = function(callback) { global.selfGet = function(callback) {
var json = { var json = {
"session": global.cookieGet("session"), "ownerToken": global.cookieGet("ownerToken"),
}; };
global.post(global.commentoOrigin + "/api/owner/self", json, function(resp) { global.post(global.commentoOrigin + "/api/owner/self", json, function(resp) {