stufflog graphql server
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.

227 lines
5.8 KiB

  1. package services
  2. import (
  3. "context"
  4. "errors"
  5. "git.aiterp.net/stufflog/server/database/repositories"
  6. "git.aiterp.net/stufflog/server/internal/generate"
  7. "git.aiterp.net/stufflog/server/internal/xlerrors"
  8. "git.aiterp.net/stufflog/server/models"
  9. "github.com/gin-gonic/gin"
  10. "math/rand"
  11. "time"
  12. )
  13. var ErrLoginFailed = errors.New("login failed")
  14. var ErrInternalLoginFailure = errors.New("login failed due to internal error")
  15. var ErrInternalPermissionFailure = errors.New("permission check failed due to internal error")
  16. var ErrWrongCurrentPassword = errors.New("current password is missing")
  17. var ErrMissingCurrentPassword = errors.New("current password is missing")
  18. var authCookieName = "stufflog_cookie"
  19. var authCtxKey = "stufflog.auth"
  20. var ginCtxKey = "stufflog.gin"
  21. type Auth struct {
  22. users repositories.UserRepository
  23. session repositories.SessionRepository
  24. projects repositories.ProjectRepository
  25. }
  26. func (auth *Auth) Login(ctx context.Context, username, password string) (*models.User, error) {
  27. user, err := auth.users.Find(ctx, username)
  28. if err != nil {
  29. select {
  30. case <-time.After(time.Millisecond * time.Duration(rand.Int63n(100)+100)):
  31. case <-ctx.Done():
  32. }
  33. return nil, ErrLoginFailed
  34. }
  35. if !user.CheckPassword(password) {
  36. select {
  37. case <-time.After(time.Millisecond * time.Duration(rand.Int63n(50))):
  38. case <-ctx.Done():
  39. }
  40. return nil, ErrLoginFailed
  41. }
  42. session := models.Session{
  43. ID: generate.SessionID(),
  44. UserID: user.ID,
  45. ExpiryTime: time.Now().Add(time.Hour * 168),
  46. }
  47. err = auth.session.Save(ctx, session)
  48. if err != nil {
  49. return nil, ErrInternalLoginFailure
  50. }
  51. if c := ctx.Value(ginCtxKey).(*gin.Context); c != nil {
  52. c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true)
  53. } else {
  54. return nil, ErrInternalLoginFailure
  55. }
  56. return user, nil
  57. }
  58. func (auth *Auth) Logout(ctx context.Context) (*models.User, error) {
  59. user := auth.UserFromContext(ctx)
  60. if user == nil {
  61. return nil, xlerrors.PermissionDenied
  62. }
  63. c, ok := ctx.Value(ginCtxKey).(*gin.Context)
  64. if !ok {
  65. return nil, ErrInternalLoginFailure
  66. }
  67. c.SetCookie(authCookieName, "", 0, "/", "", false, true)
  68. return user, nil
  69. }
  70. func (auth *Auth) CreateUser(ctx context.Context, username, password, name string, active, admin bool) (*models.User, error) {
  71. loggedInUser := auth.UserFromContext(ctx)
  72. if loggedInUser == nil || !loggedInUser.Admin {
  73. return nil, xlerrors.PermissionDenied
  74. }
  75. user := &models.User{
  76. ID: username,
  77. Name: name,
  78. Active: active,
  79. Admin: admin,
  80. }
  81. err := user.SetPassword(password)
  82. if err != nil {
  83. return nil, err
  84. }
  85. user, err = auth.users.Insert(ctx, *user)
  86. if err != nil {
  87. return nil, err
  88. }
  89. return user, nil
  90. }
  91. func (auth *Auth) UserFromContext(ctx context.Context) *models.User {
  92. user, _ := ctx.Value(authCtxKey).(*models.User)
  93. return user
  94. }
  95. func (auth *Auth) ProjectPermission(ctx context.Context, project models.Project) (*models.ProjectPermission, error) {
  96. user := auth.UserFromContext(ctx)
  97. if user == nil || !user.Active {
  98. return nil, xlerrors.PermissionDenied
  99. }
  100. permission, err := auth.projects.GetPermission(ctx, project, *user)
  101. if err != nil {
  102. return nil, ErrInternalPermissionFailure
  103. }
  104. if permission.Level == models.ProjectPermissionLevelNoAccess {
  105. return nil, xlerrors.PermissionDenied
  106. }
  107. return permission, nil
  108. }
  109. func (auth *Auth) IssuePermission(ctx context.Context, issue models.Issue) (*models.ProjectPermission, error) {
  110. user := auth.UserFromContext(ctx)
  111. if user == nil || !user.Active {
  112. return nil, xlerrors.PermissionDenied
  113. }
  114. isOwnedOrAssigned := issue.AssigneeID == user.ID || issue.OwnerID == user.ID
  115. permission, err := auth.projects.GetIssuePermission(ctx, issue, *user)
  116. if err != nil {
  117. return nil, ErrInternalPermissionFailure
  118. }
  119. if permission.Level == models.ProjectPermissionLevelNoAccess {
  120. return nil, xlerrors.PermissionDenied
  121. }
  122. if !(permission.CanViewAnyIssue() || (permission.CanViewOwnIssue() && isOwnedOrAssigned)) {
  123. return nil, xlerrors.PermissionDenied
  124. }
  125. return permission, nil
  126. }
  127. func (auth *Auth) CheckGinSession(c *gin.Context) {
  128. ctx := context.WithValue(c.Request.Context(), ginCtxKey, c)
  129. cookie, err := c.Cookie(authCookieName)
  130. if err != nil {
  131. c.Request = c.Request.WithContext(ctx)
  132. return
  133. }
  134. session, err := auth.session.Find(c.Request.Context(), cookie)
  135. if err != nil {
  136. c.Request = c.Request.WithContext(ctx)
  137. return
  138. }
  139. if time.Until(session.ExpiryTime) < time.Hour*167 {
  140. session.ExpiryTime = time.Now().Add(time.Hour * 168)
  141. _ = auth.session.Save(c.Request.Context(), *session)
  142. c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true)
  143. }
  144. user, err := auth.users.Find(c.Request.Context(), session.UserID)
  145. if err != nil {
  146. c.Request = c.Request.WithContext(ctx)
  147. return
  148. }
  149. ctx = context.WithValue(ctx, authCtxKey, user)
  150. c.Request = c.Request.WithContext(ctx)
  151. }
  152. func (auth *Auth) EditUser(ctx context.Context, username string, setName *string, currentPassword *string, newPassword *string) (*models.User, error) {
  153. loggedInUser := auth.UserFromContext(ctx)
  154. if loggedInUser == nil {
  155. return nil, xlerrors.PermissionDenied
  156. }
  157. user, err := auth.users.Find(ctx, username)
  158. if err != nil {
  159. return nil, err
  160. }
  161. if user.ID != loggedInUser.ID && !loggedInUser.Admin {
  162. return nil, xlerrors.PermissionDenied
  163. }
  164. if newPassword != nil {
  165. // Only require current password if it's given, or if the user is NOT an admin changing
  166. // another user's password
  167. if currentPassword != nil || !(loggedInUser.Admin && loggedInUser.ID != user.ID) {
  168. if currentPassword == nil {
  169. return nil, ErrMissingCurrentPassword
  170. }
  171. if !user.CheckPassword(*currentPassword) {
  172. return nil, ErrWrongCurrentPassword
  173. }
  174. }
  175. err = user.SetPassword(*newPassword)
  176. if err != nil {
  177. return nil, err
  178. }
  179. }
  180. if setName != nil && *setName != "" {
  181. user.Name = *setName
  182. }
  183. err = auth.users.Save(ctx, *user)
  184. if err != nil {
  185. return nil, err
  186. }
  187. return user, nil
  188. }