The backend for the project formerly known as signet, now known as beignet.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
преди 2 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package endpoints
  2. import (
  3. "crypto/rand"
  4. "crypto/subtle"
  5. "encoding/base64"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "net/http"
  10. "strings"
  11. "github.com/imosed/signet/auth"
  12. . "github.com/imosed/signet/data"
  13. "github.com/rs/zerolog/log"
  14. "github.com/spf13/viper"
  15. "golang.org/x/crypto/argon2"
  16. )
  17. const (
  18. SuperUser uint = iota
  19. AdminPlus
  20. Admin
  21. )
  22. type Params struct {
  23. Iterations uint32
  24. Memory uint32
  25. Parallelism uint8
  26. SaltLength uint32
  27. KeyLength uint32
  28. }
  29. func GenerateRandomBytes(n uint32) ([]byte, error) {
  30. var b = make([]byte, n)
  31. _, err := rand.Read(b)
  32. if err != nil {
  33. return nil, err
  34. }
  35. return b, nil
  36. }
  37. func GenerateHash(password string, p *Params) (encodedHash string, err error) {
  38. salt, err := GenerateRandomBytes(p.SaltLength)
  39. if err != nil {
  40. return "", err
  41. }
  42. var hash = argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)
  43. var bSalt = base64.RawStdEncoding.EncodeToString(salt)
  44. var bHash = base64.RawStdEncoding.EncodeToString(hash)
  45. encodedHash = fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", argon2.Version, p.Memory, p.Iterations, p.Parallelism, bSalt, bHash)
  46. return encodedHash, nil
  47. }
  48. func ComparePasswordAndHash(password, encodedHash string) (match bool, err error) {
  49. p, salt, hash, err := DecodeHash(encodedHash)
  50. if err != nil {
  51. return false, err
  52. }
  53. var otherHash = argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)
  54. if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
  55. return true, nil
  56. }
  57. return false, nil
  58. }
  59. func DecodeHash(encodedHash string) (p *Params, salt, hash []byte, err error) {
  60. var details = strings.Split(encodedHash, "$")
  61. if len(details) != 6 {
  62. return nil, nil, nil, errors.New("the encoded hash is not in the correct format")
  63. }
  64. var version int
  65. _, err = fmt.Sscanf(details[2], "v=%d", &version)
  66. if err != nil {
  67. return nil, nil, nil, err
  68. }
  69. if version != argon2.Version {
  70. return nil, nil, nil, errors.New("the version of argon2 does not match the hash string")
  71. }
  72. p = &Params{}
  73. _, err = fmt.Sscanf(details[3], "m=%d,t=%d,p=%d", &p.Memory, &p.Iterations, &p.Parallelism)
  74. if err != nil {
  75. return nil, nil, nil, err
  76. }
  77. salt, err = base64.RawStdEncoding.DecodeString(details[4])
  78. if err != nil {
  79. return nil, nil, nil, err
  80. }
  81. p.SaltLength = uint32(len(salt))
  82. hash, err = base64.RawStdEncoding.DecodeString(details[5])
  83. if err != nil {
  84. return nil, nil, nil, err
  85. }
  86. p.KeyLength = uint32(len(hash))
  87. return p, salt, hash, nil
  88. }
  89. type AuthenticationRequest struct {
  90. Username string `json:"username"`
  91. Password string `json:"password"`
  92. }
  93. func noUsersRegistered() bool {
  94. results := Db.Find(&User{})
  95. return results.RowsAffected == 0
  96. }
  97. func determinePrivileges() uint {
  98. if noUsersRegistered() {
  99. return SuperUser
  100. } else {
  101. return Admin
  102. }
  103. }
  104. func GetHashedPassword(password string) (encodedHash string, err error) {
  105. hash, err := GenerateHash(password, &Params{
  106. Memory: uint32(viper.GetInt("hashing.memory")),
  107. Iterations: uint32(viper.GetInt("hashing.iterations")),
  108. Parallelism: uint8(viper.GetInt("hashing.parallelism")),
  109. SaltLength: uint32(viper.GetInt("hashing.saltLength")),
  110. KeyLength: uint32(viper.GetInt("hashing.keyLength")),
  111. })
  112. return hash, err
  113. }
  114. func Register(w http.ResponseWriter, r *http.Request) {
  115. var req AuthenticationRequest
  116. err := json.NewDecoder(r.Body).Decode(&req)
  117. if err != nil {
  118. log.Error().Err(err).Msg("Could not decode body in Register call")
  119. return
  120. }
  121. var claims *auth.Claims
  122. claims, err = auth.GetUserClaims(r)
  123. if err != nil {
  124. log.Error().Err(err).Msg("Could not determine if user is authenticated")
  125. return
  126. }
  127. if !noUsersRegistered() {
  128. if claims == nil {
  129. w.WriteHeader(401)
  130. return
  131. }
  132. if claims.Privileges > AdminPlus {
  133. w.WriteHeader(403)
  134. return
  135. }
  136. }
  137. hash, err := GetHashedPassword(req.Password)
  138. if err != nil {
  139. log.Error().Err(err).Msg("Could not generate hash for registration")
  140. return
  141. }
  142. Db.Create(&User{
  143. Username: req.Username,
  144. Password: hash,
  145. Privileges: determinePrivileges(),
  146. })
  147. err = json.NewEncoder(w).Encode(SuccessResponse{Success: true})
  148. if err != nil {
  149. log.Error().Err(err).Msg("Could not deliver successful account creation response")
  150. }
  151. }