|
|
package services
import ( "context" "errors" "git.aiterp.net/stufflog/server/database/repositories" "git.aiterp.net/stufflog/server/internal/generate" "git.aiterp.net/stufflog/server/models" "github.com/gin-gonic/gin" "math/rand" "time" )
var ErrLoginFailed = errors.New("login failed") var ErrInternalLoginFailure = errors.New("login failed due to internal error") var ErrInternalPermissionFailure = errors.New("permission check failed due to internal error") var ErrPermissionDenied = errors.New("permission denied")
var authCookieName = "stufflog_cookie" var authCtxKey = "stufflog.auth" var ginCtxKey = "stufflog.gin"
type Auth struct { users repositories.UserRepository session repositories.SessionRepository projects repositories.ProjectRepository }
func (auth *Auth) Login(ctx context.Context, username, password string) (*models.User, error) { user, err := auth.users.Find(ctx, username) if err != nil { select { case <-time.After(time.Millisecond * time.Duration(rand.Int63n(100)+100)): case <-ctx.Done(): } return nil, ErrLoginFailed }
if !user.CheckPassword(password) { select { case <-time.After(time.Millisecond * time.Duration(rand.Int63n(50))): case <-ctx.Done(): } return nil, ErrLoginFailed }
session := models.Session{ ID: generate.SessionID(), UserID: user.ID, ExpiryTime: time.Now().Add(time.Hour * 168), } err = auth.session.Save(c.Request.Context(), *session) if err != nil { return nil, ErrInternalLoginFailure }
if c := ctx.Value(ginCtxKey).(*gin.Context); c != nil { c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true) } else { return nil, ErrInternalLoginFailure }
return user, nil }
func (auth *Auth) UserFromContext(ctx context.Context) *models.User { user, _ := ctx.Value(authCtxKey).(*models.User) return user }
func (auth *Auth) ProjectPermission(ctx context.Context, project models.Project) (*models.ProjectPermission, error) { user := auth.UserFromContext(ctx) if user == nil { return nil, ErrPermissionDenied }
permission, err := auth.projects.GetPermission(ctx, project, *user) if err != nil { return nil, ErrInternalPermissionFailure } if permission.Level == models.ProjectPermissionLevelNoAccess { return nil, ErrPermissionDenied }
return permission, nil }
func (auth *Auth) IssuePermission(ctx context.Context, issue models.Issue) (*models.ProjectPermission, error) { user := auth.UserFromContext(ctx) if user == nil { return nil, ErrPermissionDenied }
permission, err := auth.projects.GetIssuePermission(ctx, issue, *user) if err != nil { return nil, ErrInternalPermissionFailure } if permission.Level == models.ProjectPermissionLevelNoAccess { return nil, ErrPermissionDenied }
return permission, nil }
func (auth *Auth) CheckGinSession(c *gin.Context) { ctx := context.WithValue(c.Request.Context(), ginCtxKey, authCtxKey) defer func() { c.Request = c.Request.WithContext(ctx) }()
cookie, err := c.Cookie(authCookieName) if err != nil { return }
session, err := auth.session.Find(c.Request.Context(), cookie) if err != nil { return }
if time.Until(session.ExpiryTime) < time.Hour*167 { session.ExpiryTime = time.Now().Add(time.Hour * 168) _ = auth.session.Save(c.Request.Context(), *session) c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true) }
user, err := auth.users.Find(c.Request.Context(), cookie) if err != nil { return }
ctx = context.WithValue(ctx, authCtxKey, user) }
|