Skip to content

Commit

Permalink
Invalidate sessions when password is changed
Browse files Browse the repository at this point in the history
Store a session key in the session that we regenerate every time
the password changes. If they don't match then the session is
invalid.

If the user changes their own password, we'll update the current
session with the new key but any others will invalidate.

If an admin changes a password via /wiki/users, all sessions for
that user will be invalidated.

Closes #75
  • Loading branch information
csmith committed Apr 4, 2021
1 parent 8d85cb8 commit a16ade2
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 7 deletions.
24 changes: 22 additions & 2 deletions config/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type User struct {
Name string
Salt []byte
Password []byte
SessionKey []byte
Permissions Permission
}

Expand Down Expand Up @@ -82,13 +83,26 @@ func (a *UserManager) load() error {
return err
}

dirty := false
hasAdmin := false
for i := range settings.Users {
u := settings.Users[i]
a.users[strings.ToLower(u.Name)] = u
if u.Has(PermissionAdmin) {
hasAdmin = true
}
if u.SessionKey == nil {
key, err := a.randomBytes()
if err != nil {
return err
}
u.SessionKey = key
dirty = true
}
}

if dirty {
_ = a.save("System", "Migration: adding session keys")
}

if len(a.users) > 0 && !hasAdmin {
Expand Down Expand Up @@ -212,7 +226,7 @@ func (a *UserManager) canRemoveAdmin(user *User) bool {
return false
}

func (a *UserManager) generateSalt() ([]byte, error) {
func (a *UserManager) randomBytes() ([]byte, error) {
res := make([]byte, 16)
n, err := rand.Read(res)

Expand All @@ -224,7 +238,7 @@ func (a *UserManager) generateSalt() ([]byte, error) {
}

func (a *UserManager) setPassword(u *User, password string) error {
salt, err := a.generateSalt()
salt, err := a.randomBytes()
if err != nil {
return err
}
Expand All @@ -235,7 +249,13 @@ func (a *UserManager) setPassword(u *User, password string) error {
return err
}

key, err := a.randomBytes()
if err != nil {
return err
}

u.Salt = salt
u.Password = hash
u.SessionKey = key
return nil
}
16 changes: 11 additions & 5 deletions handlers_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"fmt"
"log"
"net/http"

Expand All @@ -10,15 +11,18 @@ import (
)

const (
sessionName = "wiki"
sessionUserKey = "user"
sessionNoticeKey = "notice"
sessionErrorKey = "error"
sessionName = "wiki"
sessionUserKey = "user"
sessionSessionKey = "session"
sessionNoticeKey = "notice"
sessionErrorKey = "error"

contextUserKey = "user"
contextErrorKey = "error"
contextNoticeKey = "notice"
contextSessionKey = "session"

sessionKeyFormat = "wiki:%x"
)

type UserProvider interface {
Expand All @@ -33,7 +37,9 @@ func SessionHandler(up UserProvider, store sessions.Store) func(http.Handler) ht
if username, ok := s.Values[sessionUserKey]; ok {
user := up.User(username.(string))
if user != nil {
request = request.WithContext(context.WithValue(request.Context(), contextUserKey, user))
if key := s.Values[sessionSessionKey]; fmt.Sprintf(sessionKeyFormat, user.SessionKey) == key {
request = request.WithContext(context.WithValue(request.Context(), contextUserKey, user))
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions handlers_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func LoginHandler(auth Authenticator) http.HandlerFunc {
putSessionKey(writer, request, sessionErrorKey, fmt.Sprintf("Failed to login: %v", err))
} else {
putSessionKey(writer, request, sessionUserKey, user.Name)
putSessionKey(writer, request, sessionSessionKey, fmt.Sprintf(sessionKeyFormat, user.SessionKey))
}
writer.Header().Set("location", redirect)
writer.WriteHeader(http.StatusSeeOther)
Expand Down Expand Up @@ -199,6 +200,7 @@ func ModifyAccountHandler(pu PasswordUpdater) http.HandlerFunc {
putSessionKey(writer, request, sessionErrorKey, fmt.Sprintf("Unable to set password: %v", err))
} else {
putSessionKey(writer, request, sessionNoticeKey, "Your password has been updated")
putSessionKey(writer, request, sessionSessionKey, fmt.Sprintf(sessionKeyFormat, user.SessionKey))
}
} else {
writer.WriteHeader(http.StatusBadRequest)
Expand Down

0 comments on commit a16ade2

Please sign in to comment.