package app import ( "context" "errors" "log" "net/netip" "strings" "evening_detective_server/internal/modules/processor_jwt" "evening_detective_server/internal/modules/roles" "evening_detective_server/internal/services/users_service" proto "evening_detective_server/proto" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) func (s *server) Signup(ctx context.Context, req *proto.SignupReq) (*proto.SignupRsp, error) { ip, userAgent := clientInfoFromContext(ctx) err := s.usersService.AddUser( ctx, req.Username, req.Email, req.AcceptTerms, req.AcceptPrivacy, ip, userAgent, ) if err != nil { return nil, userErrorStatus(err, "не удалось зарегистрировать пользователя") } return &proto.SignupRsp{}, nil } func (s *server) DeleteAccount(ctx context.Context, _ *proto.DeleteAccountReq) (*proto.DeleteAccountRsp, error) { claims, ok := processor_jwt.ClaimsFromContext(ctx) if !ok { return nil, status.Errorf(codes.Unauthenticated, "claims is not provided") } password := "" if md, ok := metadata.FromIncomingContext(ctx); ok { if values := md.Get("x-password"); len(values) > 0 { password = values[0] } } err := s.usersService.DeleteAccount(ctx, claims.UserID, password) if err != nil { return nil, userErrorStatus(err, "не удалось удалить аккаунт") } return &proto.DeleteAccountRsp{}, nil } // (runtime.annotateContext / runtime.DefaultHeaderMatcher): // // grpcgateway-; func clientInfoFromContext(ctx context.Context) (ip string, userAgent string) { md, ok := metadata.FromIncomingContext(ctx) if !ok { return "", "" } if values := md.Get("x-real-ip"); len(values) > 0 { if v := strings.TrimSpace(values[0]); v != "" { if _, err := netip.ParseAddr(v); err == nil { ip = v } } } if ip == "" { if values := md.Get("x-forwarded-for"); len(values) > 0 { parts := strings.Split(values[0], ",") for i := len(parts) - 1; i >= 0; i-- { v := strings.TrimSpace(parts[i]) if v == "" { continue } if _, err := netip.ParseAddr(v); err == nil { ip = v break } } } } if values := md.Get("grpcgateway-user-agent"); len(values) > 0 { userAgent = values[0] } else if values := md.Get("user-agent"); len(values) > 0 { userAgent = values[0] } return ip, userAgent } func (s *server) RefreshPassword(ctx context.Context, req *proto.RefreshPasswordReq) (*proto.RefreshPasswordRsp, error) { err := s.usersService.RefreshPassword(ctx, req.Email) if err != nil { return nil, userErrorStatus(err, "не удалось обновить пароль") } return &proto.RefreshPasswordRsp{}, nil } func (s *server) Login(ctx context.Context, req *proto.LoginReq) (*proto.LoginRsp, error) { accessToken, refreshToken, err := s.usersService.Login(ctx, req.Email, req.Password) if err != nil { return nil, userErrorStatus(err, "не удалось выполнить вход") } return &proto.LoginRsp{ AccessToken: accessToken, RefreshToken: refreshToken, }, nil } func (s *server) Refresh(ctx context.Context, req *proto.RefreshReq) (*proto.RefreshRsp, error) { accessToken, refreshToken, err := s.usersService.Refresh(ctx, req.RefreshToken) if err != nil { if errors.Is(err, users_service.ErrInvalidRefreshToken) { return nil, status.Error(codes.Unauthenticated, err.Error()) } return nil, userErrorStatus(err, "не удалось обновить сессию") } return &proto.RefreshRsp{ AccessToken: accessToken, RefreshToken: refreshToken, }, nil } func (s *server) GetUsers(ctx context.Context, _ *proto.GetUsersReq) (*proto.GetUsersRsp, error) { claims, claimsErr := claimsFromContext(ctx) if claimsErr != nil { return nil, claimsErr } if !roles.HasRole(claims, roles.Admin) { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } users, err := s.usersService.GetUsers(ctx) if err != nil { return nil, userErrorStatus(err, "не удалось получить пользователей") } resUsers := make([]*proto.User, 0, len(users)) for _, user := range users { resUsers = append(resUsers, mapUser(user)) } return &proto.GetUsersRsp{ Users: resUsers, }, nil } func (s *server) GetUserByID(ctx context.Context, req *proto.GetUserByIDReq) (*proto.GetUserByIDRsp, error) { user, err := s.usersService.GetUserByID(ctx, int(req.ID)) if err != nil { return nil, userErrorStatus(err, "не удалось получить пользователя") } return &proto.GetUserByIDRsp{ User: mapUser(user), }, nil } func (s *server) GetMe(ctx context.Context, _ *proto.GetMeReq) (*proto.GetMeRsp, error) { claims, claimsErr := claimsFromContext(ctx) if claimsErr != nil { return nil, claimsErr } user, err := s.usersService.GetUserByID(ctx, claims.UserID) if err != nil { return nil, userErrorStatus(err, "не удалось получить профиль") } return &proto.GetMeRsp{ User: mapUser(user), }, nil } func (s *server) AddUserRole(ctx context.Context, req *proto.AddUserRoleReq) (*proto.AddUserRoleRsp, error) { claims, claimsErr := claimsFromContext(ctx) if claimsErr != nil { return nil, claimsErr } if !roles.HasRole(claims, roles.Admin) { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } if err := s.usersService.AddUserRole(ctx, int(req.ID), req.Role); err != nil { return nil, userErrorStatus(err, "не удалось добавить роль") } return &proto.AddUserRoleRsp{}, nil } func (s *server) DeleteUserRole(ctx context.Context, req *proto.DeleteUserRoleReq) (*proto.DeleteUserRoleRsp, error) { claims, claimsErr := claimsFromContext(ctx) if claimsErr != nil { return nil, claimsErr } if !roles.HasRole(claims, roles.Admin) { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } if err := s.usersService.DeleteUserRole(ctx, int(req.ID), req.Role); err != nil { return nil, userErrorStatus(err, "не удалось удалить роль") } return &proto.DeleteUserRoleRsp{}, nil } func (s *server) GetPermissions(ctx context.Context, _ *proto.GetPermissionsReq) (*proto.GetPermissionsRsp, error) { claims, claimsErr := claimsFromContext(ctx) if claimsErr != nil { return nil, claimsErr } permissions := s.uiService.GetPermissions(claims.Roles) return &proto.GetPermissionsRsp{ Permissions: permissions, }, nil } func (s *server) ChangeUserPassword(ctx context.Context, req *proto.ChangeUserPasswordReq) (*proto.ChangeUserPasswordRsp, error) { claims, ok := processor_jwt.ClaimsFromContext(ctx) if !ok || claims == nil { log.Printf("change user password failed: authentication missing, target_user_id=%d", req.GetID()) return nil, status.Error(codes.Unauthenticated, "Требуется авторизация") } if !roles.HasRole(claims, roles.Admin) { log.Printf("change user password failed: admin role required, actor_user_id=%d target_user_id=%d", claims.UserID, req.GetID()) return nil, status.Error(codes.PermissionDenied, "Недостаточно прав") } err := s.usersService.ChangeUserPassword(ctx, claims.UserID, int(req.GetID()), req.GetNewPassword()) if err != nil { log.Printf("change user password failed: actor_user_id=%d target_user_id=%d: %v", claims.UserID, req.GetID(), err) } switch { case errors.Is(err, users_service.ErrPasswordChangeForbidden): return nil, status.Error(codes.PermissionDenied, err.Error()) case errors.Is(err, users_service.ErrInvalidPasswordChange): return nil, status.Error(codes.InvalidArgument, err.Error()) case errors.Is(err, users_service.ErrUserNotFound): return nil, status.Error(codes.NotFound, err.Error()) case err != nil: return nil, status.Error(codes.Internal, "Не удалось изменить пароль") } return &proto.ChangeUserPasswordRsp{}, nil }