// InfosSession get infos about the session (including remaining time) func InfosSession(handle tools.Handle, db *mgo.DbQueue) (interface{}, error) { var resp InfosSessionResponse var session models.Session sid := handle.P.ByName("token") if sid == "" { return nil, tools.NewError(nil, 400, "bad request: missing token") } if tools.CheckID(sid) == false { return nil, tools.NewError(nil, 400, "bad request: invalid token") } session.IDFromHex(sid) err := session.Get(db) if err != nil { return nil, err } remaining := int(session.Expire - time.Now().Unix()) if remaining <= 0 { return nil, tools.NewError(nil, 404, "not found: session is expired") } resp.Status = "ok" resp.Session.UserID = session.UserID.Hex() resp.Session.Domain = session.Domain resp.Session.Expire = session.Expire resp.Session.Remaining = remaining return resp, nil }
// Check if a session is expired, and if it grants access to the specified domain func Check(handle tools.Handle, db *mgo.DbQueue) (interface{}, error) { var q CheckRequest err := rest.Parse(handle.R, &q) if err != nil { return nil, tools.NewError(err, 400, "bad request: couldn't parse body") } return CheckSession(q, db) }
// Logoff deletes a session (expired or not) func Logoff(handle tools.Handle, db *mgo.DbQueue) (interface{}, error) { var session models.Session sid := handle.P.ByName("token") if sid == "" { return nil, tools.NewError(nil, 400, "bad request: missing token") } if tools.CheckID(sid) == false { return nil, tools.NewError(nil, 400, "bad request: invalid token") } session.IDFromHex(sid) err := session.Delete(db) if err != nil { return nil, err } return LogoffResponse{"ok"}, nil }
// Clean every expired sessions older than age func Clean(handle tools.Handle, db *mgo.DbQueue) (interface{}, error) { sAge := handle.P.ByName("age") if sAge == "" { return nil, tools.NewError(nil, 400, "bad request: age is missing") } age, err := strconv.ParseInt(sAge, 10, 64) if err != nil { return nil, tools.NewError(err, 400, "bad request: invalid age") } n, err := models.CleanSessions(db, age) if err != nil { return nil, err } return CleanResponse{ Status: "ok", Deleted: n, }, nil }
// Delete a session in database func (s *Session) Delete(db *mgo.DbQueue) error { n, err := db.Count("sessions", mgo.M{"_id": s.ID}) if err != nil { return err } if n == 0 { return tools.NewError(nil, 404, "not found: session does not exist") } err = db.RemoveID("sessions", s.ID) return err }
// Delete user from database func (u User) Delete(db *mgo.DbQueue) error { n, err := db.CountID("users", u.ID) if err != nil { return err } if n == 0 { return tools.NewError(nil, 404, "not found: user does not exist") } err = db.RemoveID("users", u.ID) return err }
func (u User) Login(username, password, domain string, lifespan int64, db *mgo.DbQueue) (Session, error) { var s Session var err error u.Username, err = govalidator.NormalizeEmail(username) if err != nil { return s, tools.NewError(nil, 400, "bad request: username must be a valid email") } u.Password = password if password == "" { return s, tools.NewError(nil, 400, "bad request: password is missing") } ok, err := u.Check(db) if err != nil { return s, err } if ok == false { return s, tools.NewError(nil, 403, "forbidden: invalid user or password") } if u.Enable == false { return s, tools.NewError(nil, 403, "forbidden: user is diabled") } ok = u.CheckDomain(domain) if ok == false { return s, tools.NewError(nil, 403, "forbidden: restricted domain") } s.UserID = u.ID s.Domain = domain _, err = s.Create(db, lifespan) if err != nil { return s, err } return s, nil }
// InfosUser returns informations about the user. It does not lists its sessions, yet (TODO) func InfosUser(handle tools.Handle, db *mgo.DbQueue) (interface{}, error) { var user models.User uid := handle.P.ByName("uid") if handle.C.Public == true { ret, err := CheckSession(CheckRequest{ Domain: "/io/konek/app/user", Token: handle.Sid, }, db) if err != nil { return nil, err } sess := ret.(CheckResponse) if uid == "" { uid = sess.Session.UserID } if sess.Session.UserID != uid { return nil, tools.NewError(nil, 403, "forbiden: this is not your account") } } if tools.CheckID(uid) == false { return nil, tools.NewError(nil, 400, "bad request: invalid userID") } user.IDFromHex(uid) err := user.Get(db) if err != nil { return nil, err } user.Password = "" user.Salt = "" return InfosUserResponse{ Status: "ok", Infos: user, }, nil }
// Create a new user in database. // (generates Salt) func (u *User) Create(db *mgo.DbQueue) (bson.ObjectId, error) { n, err := db.Count("users", bson.M{"username": u.Username}) if err != nil { return "", err } if n != 0 { return "", tools.NewError(nil, 409, "duplicate: user already exists") } u.ID = bson.NewObjectId() u.Salt, err = tools.GenSalt(12) if err != nil { return "", err } u.Password = tools.PasswordHash(u.Username, u.Password, u.Salt) err = db.Insert("users", u) return u.ID, err }
// Update user in database. (update salt and password if needed) func (u User) Update(db *mgo.DbQueue) error { var user User err := db.FindOneID("users", &user, u.ID) if err != nil { return err } if u.Username != "" { if u.Password == "" { return tools.NewError(nil, 400, "bad request: need password to update username") } user.Username = u.Username user.Salt, err = tools.GenSalt(12) if err != nil { return err } user.Password = tools.PasswordHash(user.Username, u.Password, user.Salt) } if u.Password != "" { user.Salt, err = tools.GenSalt(12) if err != nil { return err } user.Password = tools.PasswordHash(user.Username, u.Password, user.Salt) } if u.Domains != nil { user.Domains = u.Domains } if u.Variables != nil { user.Variables = u.Variables } user.Enable = u.Enable err = db.Push(func(db *mgo.Database, ec chan error) { ec <- db.C("users").UpdateId(u.ID, user) }) return err }
// CreateUser create a new user. Checks for duplicate users and password-length requirement func CreateUser(handle tools.Handle, db *mgo.DbQueue) (interface{}, error) { var user models.User user.Enable = true user.Domains = nil user.Variables = nil err := rest.Parse(handle.R, &user) if err != nil { return nil, tools.NewError(err, 400, "bad request: couldn't parse body") } if user.Username == "" { return nil, tools.NewError(nil, 400, "bad request: username is missing") } if user.Password == "" { return nil, tools.NewError(nil, 400, "bad request: password is missing") } if len(user.Password) < handle.C.PasswordMinLength { return nil, tools.NewError(nil, 400, "bad request: password is too short") } if user.Domains == nil || len(user.Domains) == 0 { return nil, tools.NewError(nil, 400, "bad request: domains is missing") } if user.Variables == nil { user.Variables = make(map[string]interface{}) } if govalidator.IsEmail(user.Username) == false { return nil, tools.NewError(nil, 400, "bad request: username must be a valid email") } user.Username, err = govalidator.NormalizeEmail(user.Username) if err != nil { return nil, tools.NewError(nil, 400, "bad request: username must be a valid email") } uid, err := user.Create(db) return CreateResponse{ Status: "ok", UserID: uid.Hex(), }, err }
func CheckSession(q CheckRequest, db *mgo.DbQueue) (interface{}, error) { var resp CheckResponse var session models.Session if q.Token == "" { return nil, tools.NewError(nil, 400, "bad request: token is missing") } if tools.CheckID(q.Token) == false { return nil, tools.NewError(nil, 400, "bad request: invalid token") } if q.Domain == "" { return nil, tools.NewError(nil, 400, "bad request: domain is missing") } if q.Domain == "/" { return nil, tools.NewError(nil, 400, "bad request: illegal domain") } session.IDFromHex(q.Token) err := session.Get(db) if err != nil { return nil, err } if session.Expire < time.Now().Unix() { return nil, tools.NewError(nil, 404, "not found: session is expired") } if tools.CheckDomain(q.Domain, session.Domain) == false { return nil, tools.NewError(nil, 403, "forbidden: restricted domain") } resp.Status = "ok" resp.Session.UserID = session.UserID.Hex() resp.Session.Expire = session.Expire resp.Session.Remaining = int(session.Expire - time.Now().Unix()) return resp, nil }
// Login a user, creating a new session. func Login(handle tools.Handle, db *mgo.DbQueue) (interface{}, error) { var q LoginRequest var user models.User var session models.Session var resp LoginResponse err := rest.Parse(handle.R, &q) if err != nil { return nil, tools.NewError(err, 400, "bad request: couldn't parse body") } if q.Domain == "" { return nil, tools.NewError(nil, 400, "bad request: domain is missing") } if q.Domain == "/" { return nil, tools.NewError(nil, 400, "bad request: illegal domain") } if q.Username == "" { return nil, tools.NewError(nil, 400, "bad request: username is missing") } if q.Password == "" { return nil, tools.NewError(nil, 400, "bad request: password is missing") } user.Username = q.Username user.Password = q.Password if govalidator.IsEmail(user.Username) == false { return nil, tools.NewError(nil, 400, "bad request: username must be a valid email") } user.Username, err = govalidator.NormalizeEmail(user.Username) if err != nil { return nil, tools.NewError(nil, 400, "bad request: username must be a valid email") } ok, err := user.Check(db) if err != nil { return nil, err } if ok == false { return nil, tools.NewError(nil, 403, "forbidden: invalid user or password") } if user.Enable == false { return nil, tools.NewError(nil, 403, "forbidden: user is diabled") } ok = user.CheckDomain(q.Domain) if ok == false { return nil, tools.NewError(nil, 403, "forbidden: restricted domain") } session.UserID = user.ID session.Domain = q.Domain remaining, err := session.Create(db, handle.C.SessionLifespan) if err != nil { return nil, err } resp.Status = "ok" resp.Session.Token = session.ID.Hex() resp.Session.UserID = session.UserID.Hex() resp.Session.Expire = session.Expire resp.Session.Remaining = remaining return resp, nil }