usermanager.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package usermanager
  2. import (
  3. "errors"
  4. "time"
  5. "math/rand"
  6. "git.mmnx.de/Moe/databaseutils"
  7. "git.mmnx.de/Moe/configutils"
  8. "github.com/dgrijalva/jwt-go"
  9. "github.com/kataras/iris"
  10. "golang.org/x/crypto/bcrypt"
  11. "fmt"
  12. )
  13. var (
  14. Users *[]User // stores all currently logged in users
  15. )
  16. const ( // Error constants
  17. ERR_USER_NOT_FOUND = "ERR_USER_NOT_FOUND"
  18. ERR_PASSWORD_MISMATCH = "ERR_PASSWORD_MISMATCH"
  19. ERR_SESSION_TIMED_OUT = "ERR_SESSION_TIMED_OUT"
  20. ERR_INVALID_TOKEN = "ERR_INVALID_TOKEN"
  21. )
  22. type User struct { // User
  23. ID string
  24. Username string
  25. Password string
  26. Mail string
  27. Admin string
  28. }
  29. type PageParams struct{
  30. HasError string
  31. Error string
  32. ReqDir string
  33. Admin string
  34. } // {Error: ""} // TODO: OUTSOURCE : is outsourced here now
  35. type PageUserParams struct{
  36. HasError string
  37. Error string
  38. ReqDir string
  39. Username string
  40. Email string
  41. Admin string
  42. } // {Error: ""}
  43. type PageUserParamsMessage struct{
  44. HasError string
  45. Error string
  46. ReqDir string
  47. Username string
  48. Email string
  49. Admin string
  50. Message string
  51. } // {Error: ""}
  52. func (user *User) Login(username string, password string) (string, error) {
  53. hmacSampleSecret := []byte(configutils.Conf.CryptoKey) // crypto key for JWT encryption
  54. row, err := databaseutils.DBUtil.GetRow("*", "users", "username", username) // get user from db
  55. if err != nil {
  56. if err.Error() == databaseutils.ERR_EMPTY_RESULT { // empty result -> user not found
  57. return "", errors.New(ERR_USER_NOT_FOUND)
  58. } else {
  59. return "", errors.New("Unknown error")
  60. }
  61. }
  62. err = bcrypt.CompareHashAndPassword([]byte(row[2]), []byte(password))
  63. if err == nil { // if sent' pw hash == stored pw hash
  64. expire, _ := time.ParseDuration("168h") // 7 days
  65. token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
  66. "username": username,
  67. "userid": row[0],
  68. "nbf": time.Now().Unix(),
  69. "exp": time.Now().Add(expire).Unix(),
  70. "token": "nigger", // TODO db based tokens
  71. })
  72. tokenString, _ := token.SignedString(hmacSampleSecret)
  73. user.ID = row[0]
  74. user.Username = row[1]
  75. user.Password = string(row[2])
  76. user.Mail = row[3]
  77. user.Admin = string(row[4])
  78. if err != nil {
  79. fmt.Printf("Error: ", err.Error())
  80. }
  81. *Users = append(*Users, *user) // store user in logged-in-users list
  82. return tokenString, nil // return tokenString (Cookie)
  83. } else {
  84. return "", errors.New(ERR_PASSWORD_MISMATCH) // wrong password
  85. }
  86. }
  87. func (user *User) Update() error {
  88. colsVals := make([][]string, 2)
  89. colsVals[0] = []string{"username", user.Username}
  90. colsVals[1] = []string{"password", user.Password}
  91. err := databaseutils.DBUtil.UpdateRow("users", "id", string(user.ID), colsVals)
  92. if err != nil {
  93. fmt.Println("ERROOR UPDATING: " + err.Error())
  94. }
  95. return nil
  96. }
  97. func SearchUser(userID string) int {
  98. for i := range *Users {
  99. if (*Users)[i].ID == userID {
  100. return i
  101. }
  102. }
  103. return -1
  104. }
  105. func SearchUserByUsername(username string) int {
  106. for i := range *Users {
  107. if (*Users)[i].Username == username {
  108. return i
  109. }
  110. }
  111. return -1
  112. }
  113. func VerifyUserLoggedIn(tokenString string) (bool, string, error) { // TODO renew JWT from time to time preventing expiry
  114. if tokenString == "" { // if no tokenString("Cookie") exists fail
  115. return false, "-1", errors.New(ERR_INVALID_TOKEN)
  116. }
  117. hmacSampleSecret := []byte(configutils.Conf.CryptoKey) // crypto key for JWT encryption
  118. token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
  119. if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
  120. return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
  121. }
  122. return hmacSampleSecret, nil
  123. })
  124. if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { // if token is valid
  125. if userID, ok := claims["userid"].(string); ok { // extract userID
  126. sliceID := SearchUser(userID) // verify that user has a session on the server
  127. if sliceID != -1 { // searchUser returns -1 if there's no such user
  128. return true, userID, nil // logged in, TODO: "0" template comparision dynamic
  129. } else {
  130. return false, "-1", errors.New(ERR_SESSION_TIMED_OUT) // Session probably expired - may also be faked? TODO more checks?
  131. }
  132. } else {
  133. return false, "-1", errors.New("Unknown error") // This should never happen, prolly can't convert something in claims then..
  134. }
  135. } else {
  136. 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
  137. }
  138. }
  139. func AuthHandler(ctx *iris.Context) {
  140. tokenString := ctx.GetCookie("token")
  141. isAuthed, userID, err := VerifyUserLoggedIn(tokenString)
  142. ctx.Set("userID", userID) // save userID for in-context use
  143. if err != nil {
  144. fmt.Println("Auth error: ", err.Error())
  145. }
  146. if isAuthed {
  147. ctx.Next() // successfully authed, next handler
  148. } else {
  149. if err := ctx.Render("login.html", PageParams{"1", err.Error(), "login", "0"}); err != nil {
  150. println(err.Error())
  151. } // failed to auth
  152. }
  153. }
  154. func GenerateTokens(numTokens int) []string {
  155. const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  156. tokens := make([]string, 0)
  157. dbTokens := make([][]string, 0)
  158. for i := 0; i < numTokens; i++ {
  159. b := make([]byte, 16) // 16 char long tokens
  160. for i := range b {
  161. b[i] = letterBytes[rand.Intn(len(letterBytes))]
  162. }
  163. tokens = append(tokens, string(b))
  164. //dbTokens = append(dbTokens, []string{string(b), "0"})
  165. dbTokens = [][]string{[]string{"value", string(b)}, []string{"used", "0"}}
  166. err := databaseutils.DBUtil.InsertRow("tokens", dbTokens)
  167. if err != nil {
  168. fmt.Println(err.Error())
  169. return []string{""}
  170. }
  171. // dbTokens[i] :=
  172. }
  173. // err := databaseutils.DBUtil.InsertRow("tokens", dbTokens)
  174. /*if err != nil {
  175. fmt.Println(err.Error())
  176. return []string{""}
  177. }*/
  178. return tokens
  179. }