The backend for the project formerly known as signet, now known as beignet.
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

register.go 4.0 KiB

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