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.

133 lines
3.4 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/models"
  8. "github.com/gin-gonic/gin"
  9. "math/rand"
  10. "time"
  11. )
  12. var ErrLoginFailed = errors.New("login failed")
  13. var ErrInternalLoginFailure = errors.New("login failed due to internal error")
  14. var ErrInternalPermissionFailure = errors.New("permission check failed due to internal error")
  15. var ErrPermissionDenied = errors.New("permission denied")
  16. var authCookieName = "stufflog_cookie"
  17. var authCtxKey = "stufflog.auth"
  18. var ginCtxKey = "stufflog.gin"
  19. type Auth struct {
  20. users repositories.UserRepository
  21. session repositories.SessionRepository
  22. projects repositories.ProjectRepository
  23. }
  24. func (auth *Auth) Login(ctx context.Context, username, password string) (*models.User, error) {
  25. user, err := auth.users.Find(ctx, username)
  26. if err != nil {
  27. select {
  28. case <-time.After(time.Millisecond * time.Duration(rand.Int63n(100)+100)):
  29. case <-ctx.Done():
  30. }
  31. return nil, ErrLoginFailed
  32. }
  33. if !user.CheckPassword(password) {
  34. select {
  35. case <-time.After(time.Millisecond * time.Duration(rand.Int63n(50))):
  36. case <-ctx.Done():
  37. }
  38. return nil, ErrLoginFailed
  39. }
  40. session := models.Session{
  41. ID: generate.SessionID(),
  42. UserID: user.ID,
  43. ExpiryTime: time.Now().Add(time.Hour * 168),
  44. }
  45. err = auth.session.Save(c.Request.Context(), *session)
  46. if err != nil {
  47. return nil, ErrInternalLoginFailure
  48. }
  49. if c := ctx.Value(ginCtxKey).(*gin.Context); c != nil {
  50. c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true)
  51. } else {
  52. return nil, ErrInternalLoginFailure
  53. }
  54. return user, nil
  55. }
  56. func (auth *Auth) UserFromContext(ctx context.Context) *models.User {
  57. user, _ := ctx.Value(authCtxKey).(*models.User)
  58. return user
  59. }
  60. func (auth *Auth) ProjectPermission(ctx context.Context, project models.Project) (*models.ProjectPermission, error) {
  61. user := auth.UserFromContext(ctx)
  62. if user == nil {
  63. return nil, ErrPermissionDenied
  64. }
  65. permission, err := auth.projects.GetPermission(ctx, project, *user)
  66. if err != nil {
  67. return nil, ErrInternalPermissionFailure
  68. }
  69. if permission.Level == models.ProjectPermissionLevelNoAccess {
  70. return nil, ErrPermissionDenied
  71. }
  72. return permission, nil
  73. }
  74. func (auth *Auth) IssuePermission(ctx context.Context, issue models.Issue) (*models.ProjectPermission, error) {
  75. user := auth.UserFromContext(ctx)
  76. if user == nil {
  77. return nil, ErrPermissionDenied
  78. }
  79. permission, err := auth.projects.GetIssuePermission(ctx, issue, *user)
  80. if err != nil {
  81. return nil, ErrInternalPermissionFailure
  82. }
  83. if permission.Level == models.ProjectPermissionLevelNoAccess {
  84. return nil, ErrPermissionDenied
  85. }
  86. return permission, nil
  87. }
  88. func (auth *Auth) CheckGinSession(c *gin.Context) {
  89. ctx := context.WithValue(c.Request.Context(), ginCtxKey, authCtxKey)
  90. defer func() {
  91. c.Request = c.Request.WithContext(ctx)
  92. }()
  93. cookie, err := c.Cookie(authCookieName)
  94. if err != nil {
  95. return
  96. }
  97. session, err := auth.session.Find(c.Request.Context(), cookie)
  98. if err != nil {
  99. return
  100. }
  101. if time.Until(session.ExpiryTime) < time.Hour*167 {
  102. session.ExpiryTime = time.Now().Add(time.Hour * 168)
  103. _ = auth.session.Save(c.Request.Context(), *session)
  104. c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true)
  105. }
  106. user, err := auth.users.Find(c.Request.Context(), cookie)
  107. if err != nil {
  108. return
  109. }
  110. ctx = context.WithValue(ctx, authCtxKey, user)
  111. }