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.

283 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 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.UUID = uuid.NewString()
  47. u.Email = in.Form.Email
  48. u.Password = string(bytes)
  49. tx = m.db.Create(&u)
  50. if tx.RowsAffected == 0 {
  51. log.Fatalf("Failed to save user: %v", err)
  52. return nil, errors.New("Failed to save user.")
  53. }
  54. // Return the response.
  55. return &proto.RegisterResponse{}, nil
  56. }
  57. func (m *usersServer) Login(ctx context.Context, in *proto.LoginRequest) (*proto.LoginResponse, error) {
  58. // Make sure the password is included.
  59. if in.Form.Password == nil {
  60. return nil, errors.New("Password must be included.")
  61. }
  62. // Find the user.
  63. var u models.User
  64. tx := m.db.First(&u, "email = ?", in.Form.Email)
  65. if tx.RowsAffected == 0 {
  66. return nil, errors.New("User not found.")
  67. }
  68. // Compare the passwords.
  69. if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(*in.Form.Password)); err != nil {
  70. return nil, errors.New("Passwords do not match.")
  71. }
  72. // Create a session.
  73. s := &models.Session{
  74. Token: uuid.NewString(),
  75. Expires: time.Now().AddDate(0, 0, 1).UnixNano(),
  76. UserID: u.ID,
  77. }
  78. // Save the token.
  79. tx = m.db.Create(&s)
  80. if tx.RowsAffected == 0 {
  81. return nil, errors.New("Failed to create session.")
  82. }
  83. // Return the response.
  84. return &proto.LoginResponse{
  85. Token: &proto.UserToken{
  86. Token: s.Token,
  87. Expires: &s.Expires,
  88. },
  89. }, nil
  90. }
  91. func (m *usersServer) Logout(ctx context.Context, in *proto.LogoutRequest) (*proto.LogoutResponse, error) {
  92. // Find the session.
  93. var s models.Session
  94. if tx := m.db.First(&s, "token = ?", in.Token.Token); tx.RowsAffected == 0 {
  95. return nil, errors.New("Failed to find session.")
  96. }
  97. // Expire the token.
  98. s.Expires = time.Now().UnixNano()
  99. if tx := m.db.Save(&s); tx.RowsAffected == 0 {
  100. return nil, errors.New("Failed to close session.")
  101. }
  102. return &proto.LogoutResponse{}, nil
  103. }
  104. func (m *usersServer) Authorize(ctx context.Context, in *proto.AuthorizeRequest) (*proto.AuthorizeResponse, error) {
  105. // Make sure the secrets match.
  106. if in.Secret != m.secret {
  107. return nil, errors.New("Secrets do not match.")
  108. }
  109. // Find the session.
  110. var s models.Session
  111. tx := m.db.First(&s, "token = ?", in.Token.Token)
  112. if tx.RowsAffected == 0 {
  113. return nil, errors.New("Session not found.")
  114. }
  115. // Make sure the session hasn't expired.
  116. if time.Now().UnixNano() > s.Expires {
  117. return nil, errors.New("Token is expired.")
  118. }
  119. // Find the user.
  120. var u models.User
  121. tx = m.db.Model(&models.User{}).Preload("Roles").First(&u, "id = ?", s.UserID)
  122. if tx.RowsAffected == 0 {
  123. return nil, errors.New("Failed to load roles.")
  124. }
  125. // Return the response.
  126. res := &proto.AuthorizeResponse{
  127. User: &proto.UserInfo{
  128. Id: int64(u.ID),
  129. Uuid: u.UUID,
  130. },
  131. }
  132. for _, r := range u.Roles {
  133. res.Roles = append(res.Roles, &proto.UserRole{
  134. Id: int64(r.ID),
  135. Name: r.Name,
  136. })
  137. }
  138. return res, 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. }