The backend for the project formerly known as signet, now known as beignet.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

register.go 4.2 KiB

2 lat temu
2 lat temu
2 lat temu
2 lat temu
2 lat temu
2 lat temu
2 lat temu
2 lat temu
2 lat temu
2 lat temu
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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 Register(w http.ResponseWriter, r *http.Request) {
  105. var req AuthenticationRequest
  106. err := json.NewDecoder(r.Body).Decode(&req)
  107. if err != nil {
  108. log.Error().Err(err).Msg("Could not decode body in Register call")
  109. return
  110. }
  111. var claims *auth.Claims
  112. claims, err = auth.GetUserClaims(r)
  113. if err != nil {
  114. log.Error().Err(err).Msg("Could not determine if user is authenticated")
  115. return
  116. }
  117. if noUsersRegistered() || claims.Privileges <= AdminPlus {
  118. hash, err := GenerateHash(req.Password, &Params{
  119. Memory: uint32(viper.GetInt("hashing.memory")),
  120. Iterations: uint32(viper.GetInt("hashing.iterations")),
  121. Parallelism: uint8(viper.GetInt("hashing.parallelism")),
  122. SaltLength: uint32(viper.GetInt("hashing.saltLength")),
  123. KeyLength: uint32(viper.GetInt("hashing.keyLength")),
  124. })
  125. if err != nil {
  126. log.Error().Err(err).Msg("Could not generate hash for registration")
  127. return
  128. }
  129. Db.Create(&User{
  130. Username: req.Username,
  131. Password: hash,
  132. Privileges: determinePrivileges(),
  133. })
  134. err = json.NewEncoder(w).Encode(SuccessResponse{Success: true})
  135. if err != nil {
  136. log.Error().Err(err).Msg("Could not deliver successful account creation response")
  137. }
  138. } else if !noUsersRegistered() {
  139. err = json.NewEncoder(w).Encode(SuccessResponse{Success: false})
  140. if err != nil {
  141. log.Error().Err(err).Msg("Could not deliver unsuccessful account creation response")
  142. }
  143. } else if claims.Privileges > SuperUser {
  144. w.WriteHeader(403)
  145. }
  146. }