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.

192 lines
4.5 KiB

1 year ago
1 year ago
  1. package server
  2. import (
  3. "context"
  4. "errors"
  5. "log"
  6. "time"
  7. "github.com/google/uuid"
  8. "golang.org/x/crypto/bcrypt"
  9. "git.chrishayward.xyz/x/users/proto"
  10. )
  11. type usersServer struct {
  12. proto.UsersServer
  13. secret *string
  14. users UserDB
  15. tokens TokenDB
  16. resetTokens TokenDB
  17. }
  18. func NewUsersServer(secret *string) proto.UsersServer {
  19. return &usersServer{
  20. secret: secret,
  21. users: newInMemoryUserDB(),
  22. tokens: newInMemoryTokenDB(),
  23. resetTokens: newInMemoryTokenDB(),
  24. }
  25. }
  26. func (m *usersServer) Register(ctx context.Context, in *proto.RegisterRequest) (*proto.RegisterResponse, error) {
  27. // Make sure both passwords are included and match.
  28. if in.Form.Password == nil || in.Form.PasswordAgain == nil {
  29. return nil, errors.New("Must include password(s).")
  30. }
  31. if *in.Form.Password != *in.Form.PasswordAgain {
  32. return nil, errors.New("Passwords do not match.")
  33. }
  34. // Check for an existing user.
  35. if u, _ := m.users.FindByEmail(in.Form.Email); u != nil {
  36. return nil, errors.New("User already exists.")
  37. }
  38. // Encode the password.
  39. bytes, err := bcrypt.GenerateFromPassword([]byte(*in.Form.Password), bcrypt.MaxCost)
  40. if err != nil {
  41. log.Fatalf("Failed to encode password: %v", err)
  42. return nil, errors.New("Failed to encode password.")
  43. }
  44. // Create the new user.
  45. if err := m.users.Save(&User{
  46. Email: in.Form.Email,
  47. Password: string(bytes),
  48. }); err != nil {
  49. log.Fatalf("Failed to save user: %v", err)
  50. return nil, errors.New("Failed to save user.")
  51. }
  52. // Return the response.
  53. return &proto.RegisterResponse{}, nil
  54. }
  55. func (m *usersServer) Login(ctx context.Context, in *proto.LoginRequest) (*proto.LoginResponse, error) {
  56. // Make sure the password is included.
  57. if in.Form.Password == nil {
  58. return nil, errors.New("Password must be included.")
  59. }
  60. // Find the user.
  61. user, err := m.users.FindByEmail(in.Form.Email)
  62. if err != nil {
  63. return nil, err
  64. }
  65. // Compare the passwords.
  66. if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(*in.Form.Password)); err != nil {
  67. return nil, errors.New("Passwords do not match.")
  68. }
  69. // Create a token.
  70. expires := time.Now().AddDate(0, 0, 1)
  71. token := &Token{
  72. UserID: user.ID,
  73. Token: uuid.NewString(),
  74. Expires: &expires,
  75. }
  76. // Save the token.
  77. m.tokens.Save(token)
  78. // Return the response.
  79. expiresNano := expires.UnixNano()
  80. return &proto.LoginResponse{
  81. Token: &proto.UserToken{
  82. Token: token.Token,
  83. Expires: &expiresNano,
  84. },
  85. }, nil
  86. }
  87. func (m *usersServer) Authorize(ctx context.Context, in *proto.AuthorizeRequest) (*proto.AuthorizeResponse, error) {
  88. // Make sure the secrets match.
  89. if in.Secret != *m.secret {
  90. return nil, errors.New("Secrets do not match.")
  91. }
  92. // Find the token.
  93. token, err := m.tokens.FindByToken(in.Token.Token)
  94. if err != nil {
  95. return nil, err
  96. }
  97. // Make sure the token hasn't expired.
  98. if token.Expires.After(time.Now()) {
  99. return nil, errors.New("Token is expired.")
  100. }
  101. // Return the user ID.
  102. return &proto.AuthorizeResponse{
  103. User: &proto.UserInfo{
  104. Id: int64(token.UserID),
  105. },
  106. }, nil
  107. }
  108. func (m *usersServer) ResetPassword(ctx context.Context, in *proto.ResetPasswordRequest) (*proto.ResetPasswordResponse, error) {
  109. // Find the user.
  110. user, err := m.users.FindByEmail(in.Form.Email)
  111. if err != nil {
  112. return nil, err
  113. }
  114. // Generate a reset token.
  115. expires := time.Now().AddDate(0, 0, 1)
  116. token := &Token{
  117. UserID: user.ID,
  118. Token: uuid.NewString(),
  119. Expires: &expires,
  120. }
  121. // Save the token.
  122. if err := m.resetTokens.Save(token); err != nil {
  123. return nil, err
  124. }
  125. // Return the response.
  126. return &proto.ResetPasswordResponse{
  127. Token: &proto.UserToken{
  128. Token: token.Token,
  129. },
  130. }, nil
  131. }
  132. func (m *usersServer) ChangePassword(ctx context.Context, in *proto.ChangePasswordRequest) (*proto.ChangePasswordResponse, error) {
  133. // Find the reset token.
  134. resetToken, err := m.resetTokens.FindByToken(in.Token.Token)
  135. if err != nil {
  136. return nil, err
  137. }
  138. // Find the user.
  139. user, err := m.users.FindByID(resetToken.UserID)
  140. if err != nil {
  141. return nil, err
  142. }
  143. // Update the password.
  144. bytes, err := bcrypt.GenerateFromPassword([]byte(*in.Form.Password), bcrypt.MaxCost)
  145. if err != nil {
  146. log.Fatalf("Failed to encode password: %v", err)
  147. return nil, errors.New("Failed to encode password.")
  148. }
  149. user.Password = string(bytes)
  150. if err := m.users.Save(user); err != nil {
  151. return nil, err
  152. }
  153. // Expire current token.
  154. if token, err := m.tokens.FindByUserID(user.ID); token != nil && err == nil {
  155. expires := time.Now()
  156. token.Expires = &expires
  157. _ = m.tokens.Save(token)
  158. }
  159. // Return the response.
  160. return nil, nil
  161. }