The backend for the project formerly known as signet, now known as beignet.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
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. }