usermanager.go 7.7 KB

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