usermanager.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package usermanager
  2. import (
  3. "errors"
  4. "time"
  5. "strconv"
  6. "git.mmnx.de/Moe/databaseutils"
  7. "git.mmnx.de/Moe/configutils"
  8. "github.com/dgrijalva/jwt-go"
  9. "github.com/kataras/iris"
  10. "fmt"
  11. )
  12. var (
  13. Users *[]User // stores all currently logged in users
  14. )
  15. const ( // Error constants
  16. ERR_USER_NOT_FOUND = "ERR_USER_NOT_FOUND"
  17. ERR_PASSWORD_MISMATCH = "ERR_PASSWORD_MISMATCH"
  18. ERR_SESSION_TIMED_OUT = "ERR_SESSION_TIMED_OUT"
  19. ERR_INVALID_TOKEN = "ERR_INVALID_TOKEN"
  20. )
  21. type User struct { // User
  22. ID int
  23. Username string
  24. Password string
  25. Mail string
  26. Admin bool
  27. }
  28. type pageParams struct{
  29. HasError string
  30. Error string
  31. ReqDir string
  32. } // {Error: ""} // TODO: OUTSOURCE
  33. func (user *User) Login(username string, password string) (string, error) {
  34. hmacSampleSecret := []byte(configutils.Conf.CryptoKey) // crypto key for JWT encryption
  35. row, err := databaseutils.DBUtil.GetRow("*", "users", "username", username) // get user from db
  36. if err != nil {
  37. if err.Error() == databaseutils.ERR_EMPTY_RESULT { // empty result -> user not found
  38. return "", errors.New(ERR_USER_NOT_FOUND)
  39. } else {
  40. return "", errors.New("Unknown error")
  41. }
  42. }
  43. if password == row[2] { // if sent pw == stored pw // TODO md5/crypto
  44. expire, _ := time.ParseDuration("168h") // 7 days
  45. token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
  46. "username": username,
  47. "userid": row[0],
  48. "nbf": time.Now().Unix(),
  49. "exp": time.Now().Add(expire).Unix(),
  50. "token": "nigger", // TODO db based tokens
  51. })
  52. tokenString, _ := token.SignedString(hmacSampleSecret)
  53. user.ID, _ = strconv.Atoi(row[0])
  54. user.Username = row[1]
  55. user.Mail = row[3]
  56. user.Admin, err = strconv.ParseBool(row[4])
  57. if err != nil {
  58. fmt.Printf("Error: ", err.Error())
  59. }
  60. *Users = append(*Users, *user) // store user in logged-in-users list
  61. //fmt.Printf("%v\n", *Users) // DEBUG
  62. return tokenString, nil // return tokenString (Cookie)
  63. } else {
  64. return "", errors.New(ERR_PASSWORD_MISMATCH) // wrong password
  65. }
  66. }
  67. func searchUser(userID int) int {
  68. for i := range *Users {
  69. if (*Users)[i].ID == userID {
  70. return i
  71. }
  72. }
  73. return -1
  74. }
  75. func VerifyUserLoggedIn(tokenString string) (bool, int, error) { // TODO renew JWT from time to time preventing expiry
  76. if tokenString == "" { // if no tokenString("Cookie") exists fail
  77. return false, -1, errors.New(ERR_INVALID_TOKEN)
  78. }
  79. hmacSampleSecret := []byte(configutils.Conf.CryptoKey) // crypto key for JWT encryption
  80. token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
  81. if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
  82. return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
  83. }
  84. return hmacSampleSecret, nil
  85. })
  86. if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { // if token is valid
  87. if userID, ok := claims["userid"].(string); ok { // extract userID
  88. intUserID, _ := strconv.Atoi(userID) // convert to int ... god i love scripting languages
  89. sliceID := searchUser(intUserID) // verify that user has a session on the server
  90. if sliceID != -1 { // searchUser returns -1 if there's no such user
  91. return true, intUserID, nil // logged in, TODO: "0" template comparision dynamic
  92. } else {
  93. return false, -1, errors.New(ERR_SESSION_TIMED_OUT) // Session probably expired - may also be faked? TODO more checks?
  94. }
  95. } else {
  96. return false, -1, errors.New("Unknown error") // This should never happen, prolly can't convert something in claims then..
  97. }
  98. } else {
  99. 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
  100. }
  101. }
  102. func AuthHandler(ctx *iris.Context) {
  103. tokenString := ctx.GetCookie("token")
  104. isAuthed, userID, err := VerifyUserLoggedIn(tokenString)
  105. ctx.Set("userID", userID) // save userID for in-context use
  106. if err != nil {
  107. fmt.Println("Auth error: ", err.Error())
  108. }
  109. if isAuthed {
  110. ctx.Next() // successfully authed, next handler
  111. } else {
  112. if err := ctx.Render("login.html", pageParams{"1", err.Error(), "login"}); err != nil {
  113. println(err.Error())
  114. } // failed to auth
  115. }
  116. }