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.

169 wiersze
4.0 KiB

  1. package endpoints
  2. import (
  3. "crypto/rand"
  4. "crypto/subtle"
  5. "encoding/base64"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "github.com/imosed/signet/auth"
  10. . "github.com/imosed/signet/data"
  11. "github.com/spf13/viper"
  12. "golang.org/x/crypto/argon2"
  13. "net/http"
  14. "strings"
  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 claims.Privileges <= AdminPlus || noUsersRegistered() {
  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. }