You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
3.8 KiB
182 lines
3.8 KiB
package session
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.aiterp.net/aiterp/wikiauth"
|
|
|
|
"git.aiterp.net/rpdata/api/internal/config"
|
|
"git.aiterp.net/rpdata/api/internal/store"
|
|
"github.com/globalsign/mgo"
|
|
"github.com/globalsign/mgo/bson"
|
|
)
|
|
|
|
var sessionCollection *mgo.Collection
|
|
|
|
// A Session represents a login session.
|
|
type Session struct {
|
|
mutex sync.Mutex
|
|
|
|
ID string `bson:"_id"`
|
|
Time time.Time `bson:"time"`
|
|
UserID string `bson:"userId"`
|
|
|
|
user *User
|
|
w http.ResponseWriter
|
|
}
|
|
|
|
// Load loads a session from a cookie, returning either `r` or a request
|
|
// with the session context.
|
|
func Load(w http.ResponseWriter, r *http.Request) *http.Request {
|
|
cookie, err := r.Cookie("aiterp_session")
|
|
if err != nil {
|
|
return r.WithContext(contextWithSession(r.Context(), &Session{w: w}))
|
|
}
|
|
|
|
id := cookie.Value
|
|
|
|
session := Session{}
|
|
err = sessionCollection.FindId(id).One(&session)
|
|
if err != nil || time.Since(session.Time) > time.Hour*168 {
|
|
return r.WithContext(contextWithSession(r.Context(), &Session{w: w}))
|
|
}
|
|
|
|
if session.ID != "" && time.Since(session.Time) > time.Second*30 {
|
|
session.Time = time.Now()
|
|
go sessionCollection.UpdateId(id, bson.M{"$set": bson.M{"time": session.Time}})
|
|
}
|
|
|
|
cookie.Expires = time.Now().Add(time.Hour * 168)
|
|
http.SetCookie(w, cookie)
|
|
|
|
session.w = w
|
|
|
|
return r.WithContext(contextWithSession(r.Context(), &session))
|
|
}
|
|
|
|
// Login logs a user in.
|
|
func (session *Session) Login(username, password string) error {
|
|
auth := wikiauth.New(config.Global().Wiki.URL)
|
|
|
|
err := auth.Login(username, password)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Allow bot passwords
|
|
username = strings.SplitN(username, "@", 2)[0]
|
|
|
|
data := make([]byte, 32)
|
|
_, err = rand.Read(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
session.ID = hex.EncodeToString(data)
|
|
session.UserID = username
|
|
session.Time = time.Now()
|
|
|
|
err = sessionCollection.Insert(&session)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
http.SetCookie(session.w, &http.Cookie{
|
|
Name: "aiterp_session",
|
|
Value: session.ID,
|
|
Expires: time.Now().Add(time.Hour * 2160), // 90 days
|
|
HttpOnly: true,
|
|
})
|
|
|
|
user, err := FindUser(session.UserID)
|
|
if err == mgo.ErrNotFound {
|
|
user = User{ID: username, Nick: "", Permissions: DefaultPermissions()}
|
|
|
|
err := userCollection.Insert(user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Logout logs out the session
|
|
func (session *Session) Logout() {
|
|
http.SetCookie(session.w, &http.Cookie{
|
|
Name: "aiterp_session",
|
|
Value: "",
|
|
Expires: time.Unix(0, 0),
|
|
HttpOnly: true,
|
|
})
|
|
|
|
session.mutex.Lock()
|
|
session.user = nil
|
|
session.UserID = ""
|
|
session.ID = ""
|
|
session.mutex.Unlock()
|
|
|
|
sessionCollection.RemoveId(session.ID)
|
|
}
|
|
|
|
// User gets the user information for the session.
|
|
func (session *Session) User() *User {
|
|
session.mutex.Lock()
|
|
defer session.mutex.Unlock()
|
|
|
|
if session.user != nil {
|
|
return session.user
|
|
}
|
|
|
|
if session.UserID == "" {
|
|
return nil
|
|
}
|
|
|
|
user, err := FindUser(session.UserID)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
return &user
|
|
}
|
|
|
|
// NameOrPermitted is a shorthand for checking the username OR permissions, e.g. to check
|
|
// if a logged in user can edit a certain post.
|
|
func (session *Session) NameOrPermitted(userid string, permissions ...string) bool {
|
|
if session.UserID == userid {
|
|
return true
|
|
}
|
|
|
|
user := session.User()
|
|
if user == nil {
|
|
return false
|
|
}
|
|
|
|
return user.Permitted()
|
|
}
|
|
|
|
func init() {
|
|
store.HandleInit(func(db *mgo.Database) {
|
|
sessionCollection = db.C("core.sessions")
|
|
|
|
sessionCollection.EnsureIndexKey("nick")
|
|
sessionCollection.EnsureIndexKey("userId")
|
|
|
|
err := sessionCollection.EnsureIndex(mgo.Index{
|
|
Name: "time",
|
|
Key: []string{"time"},
|
|
ExpireAfter: time.Hour * 168,
|
|
})
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
})
|
|
}
|