package transport import ( "context" "errors" "git.sch9.ru/new_gate/ms-tester/internal/lib" "git.sch9.ru/new_gate/ms-tester/internal/models" sessionv1 "git.sch9.ru/new_gate/ms-tester/pkg/go/gen/proto/session/v1" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) var defaultUser = &models.User{ UserId: nil, Role: models.RoleSpectator.AsPointer(), UpdatedAt: nil, } func extractToken(ctx context.Context) (string, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { return "", errors.New("no metadata") // FIXME } tokens := md.Get("token") if len(tokens) == 0 { return "", errors.New("no token in metadata") // FIXME } token := tokens[0] if token == "" { return "", errors.New("empty token in metadata") // FIXME } return token, nil } func (s *TesterServer) readSessionAndReadUser(ctx context.Context, token string) (*models.User, error) { // FIXME: possible bottle neck: should we cache it? (think of it in future) // FIXME: maybe use single connection instead of multiple requests userId, err := s.sessionClient.Read(ctx, &sessionv1.ReadSessionRequest{Token: token}) if err != nil { return nil, status.Errorf(codes.Unauthenticated, "") // FIXME } user, err := s.userService.ReadUserById(ctx, userId.GetUserId()) // FIXME: must be cached! if err != nil { // FIXME: if error is "not found" (when error codes module is written) // means user has no record, so we should create it user = &models.User{ UserId: lib.AsInt32P(userId.GetUserId()), Role: models.RoleParticipant.AsPointer(), } err = s.userService.CreateUser(ctx, user) if err != nil { return nil, status.Errorf(codes.Unauthenticated, "") // FIXME } } return user, nil } func insertUser(ctx context.Context, user *models.User) context.Context { return context.WithValue(ctx, "user", user) } func extractUser(ctx context.Context) *models.User { return ctx.Value("user").(*models.User) } func (s *TesterServer) AuthUnaryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { token, err := extractToken(ctx) if err != nil { return handler(insertUser(ctx, defaultUser), req) } user, err := s.readSessionAndReadUser(ctx, token) if err != nil { return handler(insertUser(ctx, defaultUser), req) } return handler(insertUser(ctx, user), req) } } type ssWrapper struct { grpc.ServerStream ctx context.Context } func (s *ssWrapper) Context() context.Context { return s.ctx } func (s *TesterServer) AuthStreamInterceptor() grpc.StreamServerInterceptor { return func(server interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { ctx := ss.Context() token, err := extractToken(ctx) if err != nil { return handler(server, &ssWrapper{ServerStream: ss, ctx: insertUser(ctx, defaultUser)}) } user, err := s.readSessionAndReadUser(ctx, token) if err != nil { return handler(server, &ssWrapper{ServerStream: ss, ctx: insertUser(ctx, defaultUser)}) } return handler(server, &ssWrapper{ServerStream: ss, ctx: insertUser(ctx, user)}) } }