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.

273 lines
6.9 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
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. "gorm.io/gorm"
  10. "git.chrishayward.xyz/x/users/proto"
  11. "git.chrishayward.xyz/x/users/server/models"
  12. )
  13. type usersServer struct {
  14. proto.UsersServer
  15. secret string
  16. db *gorm.DB
  17. }
  18. func NewUsersServer(secret string, db *gorm.DB) proto.UsersServer {
  19. db.AutoMigrate(&models.User{}, &models.Role{}, &models.Session{}, &models.PasswordToken{})
  20. return &usersServer{
  21. secret: secret,
  22. db: db,
  23. }
  24. }
  25. func (m *usersServer) Register(ctx context.Context, in *proto.RegisterRequest) (*proto.RegisterResponse, error) {
  26. // Make sure both passwords are included and match.
  27. if in.Form.Password == nil || in.Form.PasswordAgain == nil {
  28. return nil, errors.New("Must include password(s).")
  29. }
  30. if *in.Form.Password != *in.Form.PasswordAgain {
  31. return nil, errors.New("Passwords do not match.")
  32. }
  33. // Check for an existing user.
  34. var u models.User
  35. if m.db.Where(&u, "email = ?", in.Form.Email).RowsAffected > 0 {
  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. u.UUID = uuid.NewString()
  46. u.Email = in.Form.Email
  47. u.Password = string(bytes)
  48. if m.db.Create(&u).RowsAffected == 0 {
  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. var u models.User
  62. if m.db.Where(&u, "email = ?", in.Form.Email).RowsAffected > 0 {
  63. return nil, errors.New("User not found.")
  64. }
  65. // Compare the passwords.
  66. if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(*in.Form.Password)); err != nil {
  67. return nil, errors.New("Passwords do not match.")
  68. }
  69. // Create a session.
  70. s := &models.Session{
  71. Token: uuid.NewString(),
  72. Expires: time.Now().AddDate(0, 0, 1).UnixNano(),
  73. UserID: u.ID,
  74. }
  75. // Save the token.
  76. if m.db.Create(&s).RowsAffected == 0 {
  77. return nil, errors.New("Failed to create session.")
  78. }
  79. // Return the response.
  80. return &proto.LoginResponse{
  81. Token: &proto.UserToken{
  82. Token: s.Token,
  83. Expires: &s.Expires,
  84. },
  85. }, nil
  86. }
  87. func (m *usersServer) Logout(ctx context.Context, in *proto.LogoutRequest) (*proto.LogoutResponse, error) {
  88. // Find the session.
  89. var s models.Session
  90. if m.db.First(&s, "token = ?", in.Token.Token).RowsAffected == 0 {
  91. return nil, errors.New("Failed to find session.")
  92. }
  93. // Expire the token.
  94. s.Expires = time.Now().UnixNano()
  95. if m.db.Save(&s).RowsAffected == 0 {
  96. return nil, errors.New("Failed to close session.")
  97. }
  98. return &proto.LogoutResponse{}, nil
  99. }
  100. func (m *usersServer) Authorize(ctx context.Context, in *proto.AuthorizeRequest) (*proto.AuthorizeResponse, error) {
  101. // Make sure the secrets match.
  102. if in.Secret != m.secret {
  103. return nil, errors.New("Secrets do not match.")
  104. }
  105. // Find the session.
  106. var s models.Session
  107. if m.db.First(&s, "token = ?", in.Token.Token).RowsAffected == 0 {
  108. return nil, errors.New("Session not found.")
  109. }
  110. // Make sure the session hasn't expired.
  111. if time.Now().UnixNano() > s.Expires {
  112. return nil, errors.New("Token is expired.")
  113. }
  114. // Find the user.
  115. var u models.User
  116. if m.db.Model(&models.User{}).Preload("Roles").First(&u, "id = ?", s.UserID).RowsAffected == 0 {
  117. return nil, errors.New("Failed to load roles.")
  118. }
  119. // Return the response.
  120. res := &proto.AuthorizeResponse{
  121. User: &proto.UserInfo{
  122. Id: int64(u.ID),
  123. Uuid: u.UUID,
  124. },
  125. }
  126. for _, r := range u.Roles {
  127. res.Roles = append(res.Roles, &proto.UserRole{
  128. Id: int64(r.ID),
  129. Name: r.Name,
  130. })
  131. }
  132. return res, nil
  133. }
  134. func (m *usersServer) ResetPassword(ctx context.Context, in *proto.ResetPasswordRequest) (*proto.ResetPasswordResponse, error) {
  135. // Find the u.
  136. var u models.User
  137. if m.db.First(&u, "email = ?", in.Form.Email).RowsAffected == 0 {
  138. return nil, errors.New("User not found.")
  139. }
  140. // Generate a reset token.
  141. rt := &models.PasswordToken{
  142. UserID: u.ID,
  143. Token: uuid.NewString(),
  144. Expires: time.Now().UnixNano(),
  145. }
  146. // Save the token.
  147. if m.db.Create(rt).RowsAffected == 0 {
  148. return nil, errors.New("Failed to create token.")
  149. }
  150. // Return the response.
  151. return &proto.ResetPasswordResponse{
  152. Token: &proto.UserToken{
  153. Token: rt.Token,
  154. },
  155. }, nil
  156. }
  157. func (m *usersServer) ChangePassword(ctx context.Context, in *proto.ChangePasswordRequest) (*proto.ChangePasswordResponse, error) {
  158. // Find the reset token.
  159. var rt models.PasswordToken
  160. if m.db.First(&rt, "token = ?", in.Token.Token).RowsAffected == 0 {
  161. return nil, errors.New("Token not found.")
  162. }
  163. // Find the user.
  164. var u models.User
  165. if m.db.First(&u, "id = ?", rt.UserID).RowsAffected == 0 {
  166. return nil, errors.New("User not found.")
  167. }
  168. // Update the password.
  169. bytes, err := bcrypt.GenerateFromPassword([]byte(*in.Form.Password), bcrypt.MaxCost)
  170. if err != nil {
  171. log.Fatalf("Failed to encode password: %v", err)
  172. return nil, errors.New("Failed to encode password.")
  173. }
  174. u.Password = string(bytes)
  175. if m.db.Save(u).RowsAffected == 0 {
  176. return nil, errors.New("Failed to update password.")
  177. }
  178. // Expire current token.
  179. rt.Expires = time.Now().UnixNano()
  180. if m.db.Save(&rt).RowsAffected == 0 {
  181. return nil, errors.New("Failed to update password.")
  182. }
  183. // Return the response.
  184. return nil, nil
  185. }
  186. func (m *usersServer) ListRoles(ctx context.Context, in *proto.ListRolesRequest) (*proto.ListRolesResponse, error) {
  187. // Make sure the secrets match.
  188. if in.Secret != m.secret {
  189. return nil, errors.New("Secrets do not match.")
  190. }
  191. // Get all of the available roles.
  192. var roles []models.Role
  193. if m.db.Find(&roles).RowsAffected == 0 {
  194. return nil, errors.New("Failed to find roles.")
  195. }
  196. // Return the response.
  197. res := &proto.ListRolesResponse{}
  198. for _, r := range roles {
  199. res.Roles = append(res.Roles, &proto.UserRole{
  200. Id: int64(r.ID),
  201. Name: r.Name,
  202. })
  203. }
  204. return res, nil
  205. }
  206. func (m *usersServer) SetRoles(ctx context.Context, in *proto.SetRolesRequest) (*proto.SetRolesResponse, error) {
  207. // Make sure the secrets match.
  208. if in.Secret != m.secret {
  209. return nil, errors.New("Secrets do not match.")
  210. }
  211. // Find the user.
  212. var u models.User
  213. if m.db.First(&u, "id = ?", in.User.Id).RowsAffected == 0 {
  214. return nil, errors.New("User not found.")
  215. }
  216. // Add the roles.
  217. var r models.Role
  218. for _, x := range in.Roles {
  219. if m.db.First(&r, "id = ?", x.Id).RowsAffected != 0 {
  220. u.Roles = append(u.Roles, &r)
  221. }
  222. }
  223. // Save the user.
  224. if tx := m.db.Save(&u); tx.RowsAffected == 0 {
  225. return nil, errors.New("Failed to add roles.")
  226. }
  227. return nil, nil
  228. }