Long overdue initial commit

This commit is contained in:
2018-06-10 14:50:06 +02:00
commit 18cb6f14aa
49 changed files with 3874 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
package session
import "context"
type contextKeyType struct{ name string }
func (ck *contextKeyType) String() string {
return ck.name
}
var contextKey = &contextKeyType{name: "session context key"}
// FromContext gets a session fron the context.
func FromContext(ctx context.Context) *Session {
return ctx.Value(contextKey).(*Session)
}
func contextWithSession(parent context.Context, session *Session) context.Context {
return context.WithValue(parent, contextKey, session)
}
+33
View File
@@ -0,0 +1,33 @@
package session
// DefaultPermissions gets the default permissions
func DefaultPermissions() []string {
return []string{
"member",
"log.edit",
"log.reorder",
"post.edit",
"post.move",
}
}
// AllPermissions gets all permissions and their purpose
func AllPermissions() map[string]string {
return map[string]string{
"member": "Can add/edit/remove own content",
"user.edit": "Can edit any users",
"character.add": "Can add any characters",
"character.edit": "Can edit any characters",
"character.remove": "Can remove any characters",
"log.add": "Can add logs",
"log.edit": "Can edit logs",
"log.remove": "Can remove logs",
"post.add": "Can add posts",
"post.edit": "Can edit posts",
"post.mvoe": "Can mvoe posts",
"post.remove": "Can remove posts",
"story.add": "Can add any stories",
"story.edit": "Can edit any stories",
"story.remove": "Can remove any stories",
}
}
+182
View File
@@ -0,0 +1,182 @@
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)
}
})
}
+44
View File
@@ -0,0 +1,44 @@
package session
import (
"git.aiterp.net/rpdata/api/internal/store"
"github.com/globalsign/mgo"
)
var userCollection *mgo.Collection
// A User represents user information about a user that has logged in.
type User struct {
ID string `bson:"_id" json:"id"`
Nick string `bson:"nick,omitempty" json:"nick,omitempty"`
Permissions []string `bson:"permissions" json:"permissions"`
}
// Permitted returns true if either of the permissions can be found
//
// `user.ID == page.Author || user.Permitted("story.edit")`
func (user *User) Permitted(permissions ...string) bool {
for i := range permissions {
for j := range user.Permissions {
if permissions[i] == user.Permissions[j] {
return true
}
}
}
return false
}
// FindUser finds a user by userid
func FindUser(userid string) (User, error) {
user := User{}
err := userCollection.FindId(userid).One(&user)
return user, err
}
func init() {
store.HandleInit(func(db *mgo.Database) {
userCollection = db.C("core.users")
})
}