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.

282 lines
7.0 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. 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. tx := m.db.First(&u, "email = ?", in.Form.Email)
  36. if tx.RowsAffected != 0 {
  37. return nil, errors.New("User already exists.")
  38. }
  39. // Encode the password.
  40. bytes, err := bcrypt.GenerateFromPassword([]byte(*in.Form.Password), bcrypt.MaxCost)
  41. if err != nil {
  42. log.Fatalf("Failed to encode password: %v", err)
  43. return nil, errors.New("Failed to encode password.")
  44. }
  45. // Create the new user.
  46. u.Email = in.Form.Email
  47. u.Password = string(bytes)
  48. tx = m.db.Create(&u)
  49. if tx.RowsAffected == 0 {
  50. log.Fatalf("Failed to save user: %v", err)
  51. return nil, errors.New("Failed to save user.")
  52. }
  53. // Return the response.
  54. return &proto.RegisterResponse{}, nil
  55. }
  56. func (m *usersServer) Login(ctx context.Context, in *proto.LoginRequest) (*proto.LoginResponse, error) {
  57. // Make sure the password is included.
  58. if in.Form.Password == nil {
  59. return nil, errors.New("Password must be included.")
  60. }
  61. // Find the user.
  62. var u models.User
  63. tx := m.db.First(&u, "email = ?", in.Form.Email)
  64. if tx.RowsAffected == 0 {
  65. return nil, errors.New("User not found.")
  66. }
  67. // Compare the passwords.
  68. if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(*in.Form.Password)); err != nil {
  69. return nil, errors.New("Passwords do not match.")
  70. }
  71. // Create a session.
  72. s := &models.Session{
  73. Token: uuid.NewString(),
  74. Expires: time.Now().AddDate(0, 0, 1).UnixNano(),
  75. UserID: u.ID,
  76. }
  77. // Save the token.
  78. tx = m.db.Create(&s)
  79. if tx.RowsAffected == 0 {
  80. return nil, errors.New("Failed to create session.")
  81. }
  82. // Return the response.
  83. return &proto.LoginResponse{
  84. Token: &proto.UserToken{
  85. Token: s.Token,
  86. Expires: &s.Expires,
  87. },
  88. }, nil
  89. }
  90. func (m *usersServer) Logout(ctx context.Context, in *proto.LogoutRequest) (*proto.LogoutResponse, error) {
  91. // Find the session.
  92. var s models.Session
  93. if tx := m.db.First(&s, "token = ?", in.Token.Token); tx.RowsAffected == 0 {
  94. return nil, errors.New("Failed to find session.")
  95. }
  96. // Expire the token.
  97. s.Expires = time.Now().UnixNano()
  98. if tx := m.db.Save(&s); tx.RowsAffected == 0 {
  99. return nil, errors.New("Failed to close session.")
  100. }
  101. return &proto.LogoutResponse{}, nil
  102. }
  103. func (m *usersServer) Authorize(ctx context.Context, in *proto.AuthorizeRequest) (*proto.AuthorizeResponse, error) {
  104. // Make sure the secrets match.
  105. if in.Secret != *m.secret {
  106. return nil, errors.New("Secrets do not match.")
  107. }
  108. // Find the session.
  109. var s models.Session
  110. tx := m.db.First(&s, "token = ?", in.Token.Token)
  111. if tx.RowsAffected == 0 {
  112. return nil, errors.New("Session not found.")
  113. }
  114. // Make sure the session hasn't expired.
  115. if time.Now().UnixNano() > s.Expires {
  116. return nil, errors.New("Token is expired.")
  117. }
  118. // Find the user.
  119. var u models.User
  120. tx = m.db.Model(&models.User{}).Preload("Roles").First(&u, "id = ?", s.UserID)
  121. if tx.RowsAffected == 0 {
  122. return nil, errors.New("Failed to load roles.")
  123. }
  124. // Return the response.
  125. res := &proto.AuthorizeResponse{
  126. User: &proto.UserInfo{},
  127. }
  128. for _, r := range u.Roles {
  129. res.Roles = append(res.Roles, &proto.UserRole{
  130. Id: int64(r.ID),
  131. Name: r.Name,
  132. })
  133. }
  134. return &proto.AuthorizeResponse{
  135. User: &proto.UserInfo{
  136. Id: int64(s.UserID),
  137. },
  138. }, nil
  139. }
  140. func (m *usersServer) ResetPassword(ctx context.Context, in *proto.ResetPasswordRequest) (*proto.ResetPasswordResponse, error) {
  141. // Find the u.
  142. var u models.User
  143. tx := m.db.First(&u, "email = ?", in.Form.Email)
  144. if tx.RowsAffected == 0 {
  145. return nil, errors.New("User not found.")
  146. }
  147. // Generate a reset token.
  148. rt := &models.PasswordToken{
  149. UserID: u.ID,
  150. Token: uuid.NewString(),
  151. Expires: time.Now().UnixNano(),
  152. }
  153. // Save the token.
  154. tx = m.db.Create(rt)
  155. if tx.RowsAffected == 0 {
  156. return nil, errors.New("Failed to create token.")
  157. }
  158. // Return the response.
  159. return &proto.ResetPasswordResponse{
  160. Token: &proto.UserToken{
  161. Token: rt.Token,
  162. },
  163. }, nil
  164. }
  165. func (m *usersServer) ChangePassword(ctx context.Context, in *proto.ChangePasswordRequest) (*proto.ChangePasswordResponse, error) {
  166. // Find the reset token.
  167. var rt models.PasswordToken
  168. tx := m.db.First(&rt, "token = ?", in.Token.Token)
  169. if tx.RowsAffected == 0 {
  170. return nil, errors.New("Token not found.")
  171. }
  172. // Find the user.
  173. var u models.User
  174. tx = m.db.First(&u, "id = ?", rt.UserID)
  175. if tx.RowsAffected == 0 {
  176. return nil, errors.New("User not found.")
  177. }
  178. // Update the password.
  179. bytes, err := bcrypt.GenerateFromPassword([]byte(*in.Form.Password), bcrypt.MaxCost)
  180. if err != nil {
  181. log.Fatalf("Failed to encode password: %v", err)
  182. return nil, errors.New("Failed to encode password.")
  183. }
  184. u.Password = string(bytes)
  185. if tx = m.db.Save(u); tx.RowsAffected == 0 {
  186. return nil, errors.New("Failed to update password.")
  187. }
  188. // Expire current token.
  189. rt.Expires = time.Now().UnixNano()
  190. if tx = m.db.Save(&rt); tx.RowsAffected == 0 {
  191. return nil, errors.New("Failed to update password.")
  192. }
  193. // Return the response.
  194. return nil, nil
  195. }
  196. func (m *usersServer) ListRoles(ctx context.Context, in *proto.ListRolesRequest) (*proto.ListRolesResponse, error) {
  197. // Make sure the secrets match.
  198. if in.Secret != *m.secret {
  199. return nil, errors.New("Secrets do not match.")
  200. }
  201. // Get all of the available roles.
  202. var roles []models.Role
  203. if tx := m.db.Find(&roles); tx.RowsAffected == 0 {
  204. return nil, errors.New("Failed to find roles.")
  205. }
  206. // Return the response.
  207. res := &proto.ListRolesResponse{}
  208. for _, r := range roles {
  209. res.Roles = append(res.Roles, &proto.UserRole{
  210. Id: int64(r.ID),
  211. Name: r.Name,
  212. })
  213. }
  214. return res, nil
  215. }
  216. func (m *usersServer) SetRoles(ctx context.Context, in *proto.SetRolesRequest) (*proto.SetRolesResponse, error) {
  217. // Make sure the secrets match.
  218. if in.Secret != *m.secret {
  219. return nil, errors.New("Secrets do not match.")
  220. }
  221. // Find the user.
  222. var u models.User
  223. if tx := m.db.First(&u, "id = ?", in.User.Id); tx.RowsAffected == 0 {
  224. return nil, errors.New("User not found.")
  225. }
  226. // Add the roles.
  227. var r models.Role
  228. for _, x := range in.Roles {
  229. if tx := m.db.First(&r, "id = ?", x.Id); tx.RowsAffected != 0 {
  230. u.Roles = append(u.Roles, r)
  231. }
  232. }
  233. // Save the user.
  234. if tx := m.db.Save(&u); tx.RowsAffected == 0 {
  235. return nil, errors.New("Failed to add roles.")
  236. }
  237. return nil, nil
  238. }