package usermanager import ( "errors" "time" "strconv" "git.mmnx.de/Moe/databaseutils" "git.mmnx.de/Moe/configutils" "github.com/dgrijalva/jwt-go" "github.com/kataras/iris" "fmt" ) var ( Users *[]User // stores all currently logged in users ) const ( // Error constants ERR_USER_NOT_FOUND = "ERR_USER_NOT_FOUND" ERR_PASSWORD_MISMATCH = "ERR_PASSWORD_MISMATCH" ERR_SESSION_TIMED_OUT = "ERR_SESSION_TIMED_OUT" ERR_INVALID_TOKEN = "ERR_INVALID_TOKEN" ) type User struct { // User ID int Username string Password string Mail string } func (user *User) Login(username string, password string) (string, error) { hmacSampleSecret := []byte(configutils.Conf.CryptoKey) // crypto key for JWT encryption row, err := databaseutils.DBUtil.GetRow("*", "users", "username", username) // get user from db if err != nil { if err.Error() == databaseutils.ERR_EMPTY_RESULT { // empty result -> user not found return "", errors.New(ERR_USER_NOT_FOUND) } else { return "", errors.New("Unknown error") } } if password == row[2] { // if sent pw == stored pw // TODO md5/crypto expire, _ := time.ParseDuration("168h") // 7 days token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "username": username, "userid": row[0], "nbf": time.Now().Unix(), "exp": time.Now().Add(expire).Unix(), "token": "nigger", // TODO db based tokens }) tokenString, _ := token.SignedString(hmacSampleSecret) user.ID, _ = strconv.Atoi(row[0]) user.Username = row[1] user.Mail = row[3] *Users = append(*Users, *user) // store user in logged-in-users list //fmt.Printf("%v\n", *Users) // DEBUG return tokenString, nil // return tokenString (Cookie) } else { return "", errors.New(ERR_PASSWORD_MISMATCH) // wrong password } } func searchUser(userID int) int { for i := range *Users { if (*Users)[i].ID == userID { return i } } return -1 } func VerifyUserLoggedIn(tokenString string) (bool, int, error) { // TODO renew JWT from time to time preventing expiry if tokenString == "" { // if no tokenString("Cookie") exists fail return false, -1, errors.New(ERR_INVALID_TOKEN) } hmacSampleSecret := []byte(configutils.Conf.CryptoKey) // crypto key for JWT encryption token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } return hmacSampleSecret, nil }) if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { // if token is valid if userID, ok := claims["userid"].(string); ok { // extract userID intUserID, _ := strconv.Atoi(userID) // convert to int ... god i love scripting languages sliceID := searchUser(intUserID) // verify that user has a session on the server if sliceID != -1 { // searchUser returns -1 if there's no such user return true, intUserID, nil // logged in } else { return false, -1, errors.New(ERR_SESSION_TIMED_OUT) // Session probably expired - may also be faked? TODO more checks? } } else { return false, -1, errors.New("Unknown error") // This should never happen, prolly can't convert something in claims then.. } } else { return false, -1, errors.New(ERR_INVALID_TOKEN) // Token is invalid, expired or whatever, TODO switch with ERR_SESSION_TIMED_OUT when database based session system } } func AuthHandler(ctx *iris.Context) { tokenString := ctx.GetCookie("token") isAuthed, userID, err := VerifyUserLoggedIn(tokenString) ctx.Set("userID", userID) // save userID for in-context use if err != nil { fmt.Println("Auth error: ", err.Error()) } if isAuthed { ctx.Next() // successfully authed, next handler } else { ctx.Render("login.html", struct{ Error string }{Error: err.Error()}) // failed to auth } }