ms-auth/internal/users/usecase/usecase.go

274 lines
7.9 KiB
Go
Raw Normal View History

2024-10-09 22:07:38 +05:00
package usecase
import (
"context"
2024-12-30 20:04:26 +05:00
"errors"
2024-10-09 22:07:38 +05:00
"git.sch9.ru/new_gate/ms-auth/config"
"git.sch9.ru/new_gate/ms-auth/internal/models"
"git.sch9.ru/new_gate/ms-auth/internal/users"
2024-12-30 20:04:26 +05:00
"git.sch9.ru/new_gate/ms-auth/pkg"
2024-10-09 22:07:38 +05:00
)
2024-12-30 20:04:26 +05:00
type UseCase struct {
userRepo users.PgRepository
sessionRepo users.ValkeyRepository
cfg config.Config
2024-10-09 22:07:38 +05:00
}
func NewUseCase(
userRepo users.PgRepository,
2024-12-30 20:04:26 +05:00
sessionRepo users.ValkeyRepository,
2024-10-09 22:07:38 +05:00
cfg config.Config,
2024-12-30 20:04:26 +05:00
) *UseCase {
return &UseCase{
userRepo: userRepo,
sessionRepo: sessionRepo,
cfg: cfg,
2024-10-09 22:07:38 +05:00
}
}
2025-02-25 18:33:15 +05:00
const (
TokenKey = "token"
)
2024-12-30 20:04:26 +05:00
func (u *UseCase) CreateUser(ctx context.Context, username string, password string, role models.Role) (int32, error) {
const op = "UseCase.CreateUser"
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
2024-10-09 22:07:38 +05:00
if !ok {
2025-02-25 18:33:15 +05:00
return 0, pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
2024-10-09 22:07:38 +05:00
}
2025-02-25 18:33:15 +05:00
if !token.Role.HasPermission(models.Create, models.ResourceAnotherUser) {
2024-12-30 20:04:26 +05:00
return 0, pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
2024-10-09 22:07:38 +05:00
}
2024-12-30 20:04:26 +05:00
id, err := u.userRepo.C().CreateUser(ctx, username, password, role)
2024-10-09 22:07:38 +05:00
if err != nil {
2024-12-30 20:04:26 +05:00
return 0, pkg.Wrap(nil, err, op, "can't create user")
2024-10-09 22:07:38 +05:00
}
2024-12-30 20:04:26 +05:00
return id, nil
2024-10-09 22:07:38 +05:00
}
2024-12-30 20:04:26 +05:00
func (u *UseCase) ReadUserById(ctx context.Context, id int32) (*models.User, error) {
const op = "UseCase.ReadUserById"
2024-10-09 22:07:38 +05:00
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
if !ok {
return nil, pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
}
if token.UserId == id && !token.Role.HasPermission(models.Read, models.ResourceMeUser) {
return nil, pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
if token.UserId != id && !token.Role.HasPermission(models.Read, models.ResourceAnotherUser) {
return nil, pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
2024-12-30 20:04:26 +05:00
user, err := u.userRepo.C().ReadUserById(ctx, id)
if err != nil {
return nil, pkg.Wrap(nil, err, op, "can't read user by id")
}
return user, nil
2024-10-09 22:07:38 +05:00
}
2025-02-25 18:33:15 +05:00
// ReadUserByUsername is for login only. There are no permission checks! DO NOT USE IT AS AN ENDPOINT RESPONSE!
2024-12-30 20:04:26 +05:00
func (u *UseCase) ReadUserByUsername(ctx context.Context, username string) (*models.User, error) {
const op = "UseCase.ReadUserByUsername"
user, err := u.userRepo.C().ReadUserByUsername(ctx, username)
if err != nil {
return nil, pkg.Wrap(nil, err, op, "can't read user by username")
}
return user, nil
2024-10-09 22:07:38 +05:00
}
2024-12-30 20:04:26 +05:00
func (u *UseCase) UpdateUser(ctx context.Context, id int32, username *string, role *models.Role) error {
const op = "UseCase.UpdateUser"
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
2024-10-09 22:07:38 +05:00
if !ok {
2025-02-25 18:33:15 +05:00
return pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
2024-10-09 22:07:38 +05:00
}
2025-02-25 18:33:15 +05:00
if token.UserId == id && !token.Role.HasPermission(models.Update, models.ResourceMeUser) {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
2024-10-09 22:07:38 +05:00
}
2025-02-25 18:33:15 +05:00
if token.UserId != id && !token.Role.HasPermission(models.Update, models.ResourceAnotherUser) {
2024-12-30 20:04:26 +05:00
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
tx, err := u.userRepo.BeginTx(ctx)
if err != nil {
return pkg.Wrap(nil, err, op, "cannot start transaction")
}
err = tx.UpdateUser(ctx, id, username, role)
if err != nil {
return pkg.Wrap(nil, errors.Join(err, tx.Rollback()), op, "cannot update user")
}
err = u.sessionRepo.DeleteAllSessions(ctx, id)
if err != nil {
return pkg.Wrap(nil, errors.Join(err, tx.Rollback()), op, "cannot delete all sessions")
}
err = tx.Commit()
if err != nil {
return pkg.Wrap(nil, err, op, "cannot commit transaction")
2024-10-09 22:07:38 +05:00
}
2024-12-30 20:04:26 +05:00
return nil
2024-10-09 22:07:38 +05:00
}
2024-12-30 20:04:26 +05:00
func (u *UseCase) DeleteUser(ctx context.Context, id int32) error {
const op = "UseCase.DeleteUser"
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
2024-10-09 22:07:38 +05:00
if !ok {
2025-02-25 18:33:15 +05:00
return pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
2024-12-30 20:04:26 +05:00
}
2025-02-25 18:33:15 +05:00
if token.UserId == id && !token.Role.HasPermission(models.Delete, models.ResourceMeUser) {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
2024-12-30 20:04:26 +05:00
}
2025-02-25 18:33:15 +05:00
if token.UserId != id && !token.Role.HasPermission(models.Delete, models.ResourceAnotherUser) {
2024-12-30 20:04:26 +05:00
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
tx, err := u.userRepo.BeginTx(ctx)
if err != nil {
return pkg.Wrap(nil, err, op, "cannot start transaction")
}
err = tx.DeleteUser(ctx, id)
if err != nil {
return pkg.Wrap(nil, errors.Join(err, tx.Rollback()), op, "cannot delete user")
}
err = u.sessionRepo.DeleteAllSessions(ctx, id)
if err != nil {
return pkg.Wrap(nil, errors.Join(err, tx.Rollback()), op, "cannot delete all sessions")
2024-10-09 22:07:38 +05:00
}
2024-12-30 20:04:26 +05:00
err = tx.Commit()
if err != nil {
return pkg.Wrap(nil, err, op, "cannot commit transaction")
}
return nil
}
2025-02-25 18:33:15 +05:00
// CreateSession is for login only. There are no permission checks! DO NOT USE IT AS AN ENDPOINT RESPONSE!
func (u *UseCase) CreateSession(ctx context.Context, userId int32, role models.Role, userAgent, ip string) (*models.Session, error) {
2024-12-30 20:04:26 +05:00
const op = "UseCase.CreateSession"
2025-02-25 18:33:15 +05:00
session, err := u.sessionRepo.CreateSession(ctx, userId, role, userAgent, ip)
2024-12-30 20:04:26 +05:00
if err != nil {
2025-02-25 18:33:15 +05:00
return nil, pkg.Wrap(nil, err, op, "cannot create session")
2024-12-30 20:04:26 +05:00
}
2025-02-25 18:33:15 +05:00
return session, nil
2024-12-30 20:04:26 +05:00
}
2025-02-25 18:33:15 +05:00
// ReadSession is for internal use only. There are no permission checks! DO NOT USE IT AS AN ENDPOINT RESPONSE!
2024-12-30 20:04:26 +05:00
func (u *UseCase) ReadSession(ctx context.Context, sessionId string) (*models.Session, error) {
const op = "UseCase.ReadSession"
session, err := u.sessionRepo.ReadSession(ctx, sessionId)
if err != nil {
return nil, pkg.Wrap(nil, err, op, "cannot read session")
}
return session, nil
}
func (u *UseCase) UpdateSession(ctx context.Context, sessionId string) error {
const op = "UseCase.UpdateSession"
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
if !ok {
return pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
}
if token.SessionId != sessionId {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
if token.SessionId == sessionId && !token.Role.HasPermission(models.Update, models.ResourceOwnSession) {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
2024-12-30 20:04:26 +05:00
err := u.sessionRepo.UpdateSession(ctx, sessionId)
if err != nil {
return pkg.Wrap(nil, err, op, "cannot update session")
}
return nil
}
func (u *UseCase) DeleteSession(ctx context.Context, sessionId string) error {
const op = "UseCase.DeleteSession"
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
if !ok {
return pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
}
if token.SessionId != sessionId {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
if token.SessionId == sessionId && !token.Role.HasPermission(models.Delete, models.ResourceOwnSession) {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
2024-12-30 20:04:26 +05:00
err := u.sessionRepo.DeleteSession(ctx, sessionId)
if err != nil {
return pkg.Wrap(nil, err, op, "cannot delete session")
}
return nil
}
2024-10-09 22:07:38 +05:00
2024-12-30 20:04:26 +05:00
func (u *UseCase) DeleteAllSessions(ctx context.Context, userId int32) error {
const op = "UseCase.DeleteAllSessions"
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
if !ok {
return pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
}
if token.UserId != userId {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
if token.UserId == userId && !token.Role.HasPermission(models.Delete, models.ResourceOwnSession) {
return pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
2024-12-30 20:04:26 +05:00
err := u.sessionRepo.DeleteAllSessions(ctx, userId)
2024-10-09 22:07:38 +05:00
if err != nil {
2024-12-30 20:04:26 +05:00
return pkg.Wrap(nil, err, op, "cannot delete all sessions")
}
return nil
}
2025-02-25 18:33:15 +05:00
func (u *UseCase) ListUsers(ctx context.Context, page int32, pageSize int32) ([]*models.User, int32, error) {
const op = "UseCase.ListUsers"
2024-12-30 20:04:26 +05:00
2025-02-25 18:33:15 +05:00
token, ok := ctx.Value(TokenKey).(*models.JWT)
if !ok {
return nil, 0, pkg.Wrap(pkg.ErrUnauthenticated, nil, op, "no token in context")
2024-12-30 20:04:26 +05:00
}
2025-02-25 18:33:15 +05:00
if !token.Role.HasPermission(models.Read, models.ResourceAnotherUser) {
return nil, 0, pkg.Wrap(pkg.NoPermission, nil, op, "no permission")
}
2024-10-09 22:07:38 +05:00
2025-02-25 18:33:15 +05:00
usersList, count, err := u.userRepo.C().ListUsers(ctx, page, pageSize)
2024-12-30 20:04:26 +05:00
if err != nil {
2025-02-25 18:33:15 +05:00
return nil, 0, pkg.Wrap(nil, err, op, "can't list users")
2024-10-09 22:07:38 +05:00
}
2025-02-25 18:33:15 +05:00
return usersList, count, nil
2024-10-09 22:07:38 +05:00
}