Loading server/console.go +22 −7 Original line number Diff line number Diff line Loading @@ -175,7 +175,7 @@ func StartConsoleServer(logger *zap.Logger, startupLogger *zap.Logger, db *sql.D serverOpts := []grpc.ServerOption{ //grpc.StatsHandler(&ocgrpc.ServerHandler{IsPublicEndpoint: true}), grpc.MaxRecvMsgSize(int(config.GetConsole().MaxMessageSizeBytes)), grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config, consoleSessionCache)), grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config, consoleSessionCache, loginAttemptCache)), } grpcServer := grpc.NewServer(serverOpts...) Loading Loading @@ -438,7 +438,7 @@ func (s *ConsoleServer) Stop() { s.grpcServer.GracefulStop() } func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache SessionCache) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) { func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache SessionCache, loginAttmeptCache LoginAttemptCache) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { if info.FullMethod == "/nakama.console.Console/Authenticate" { // Skip authentication check for Login endpoint. Loading @@ -464,7 +464,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess return nil, status.Error(codes.Unauthenticated, "Console authentication required.") } if ctx, ok = checkAuth(ctx, config, auth[0], sessionCache); !ok { if ctx, ok = checkAuth(ctx, logger, config, auth[0], sessionCache, loginAttmeptCache); !ok { return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.") } role := ctx.Value(ctxConsoleRoleKey{}).(console.UserRole) Loading @@ -478,7 +478,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess } } func checkAuth(ctx context.Context, config Config, auth string, sessionCache SessionCache) (context.Context, bool) { func checkAuth(ctx context.Context, logger *zap.Logger, config Config, auth string, sessionCache SessionCache, loginAttemptCache LoginAttemptCache) (context.Context, bool) { const basicPrefix = "Basic " const bearerPrefix = "Bearer " Loading @@ -488,9 +488,24 @@ func checkAuth(ctx context.Context, config Config, auth string, sessionCache Ses if !ok { return ctx, false } if username != config.GetConsole().Username || password != config.GetConsole().Password { // Username and/or password do not match. ip, _ := extractClientAddressFromContext(logger, ctx) if !loginAttemptCache.Allow(username, ip) { return ctx, false } if username == config.GetConsole().Username { if password != config.GetConsole().Password { // Admin password does not match. if lockout, until := loginAttemptCache.Add(config.GetConsole().Username, ip); lockout != LockoutTypeNone { switch lockout { case LockoutTypeAccount: logger.Info(fmt.Sprintf("Console admin account locked until %v.", until)) case LockoutTypeIp: logger.Info(fmt.Sprintf("Console admin IP locked until %v.", until)) } } return ctx, false } } else { return ctx, false } Loading server/console_storage_import.go +1 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ func (s *ConsoleServer) importStorage(w http.ResponseWriter, r *http.Request) { } return } ctx, ok := checkAuth(r.Context(), s.config, auth, s.consoleSessionCache) ctx, ok := checkAuth(r.Context(), s.logger, s.config, auth, s.consoleSessionCache, s.loginAttemptCache) if !ok { w.WriteHeader(401) if _, err := w.Write([]byte("Console authentication invalid.")); err != nil { Loading server/console_user.go +12 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package server import ( "bytes" "context" "database/sql" "encoding/json" "errors" "github.com/jackc/pgconn" Loading Loading @@ -118,13 +119,14 @@ func (s *ConsoleServer) dbInsertConsoleUser(ctx context.Context, in *console.Add } func (s *ConsoleServer) DeleteUser(ctx context.Context, in *console.Username) (*emptypb.Empty, error) { if deleted, err := s.dbDeleteConsoleUser(ctx, in.Username); err != nil { deleted, id, err := s.dbDeleteConsoleUser(ctx, in.Username) if err != nil { s.logger.Error("failed to delete console user", zap.Error(err), zap.String("username", in.Username)) return nil, status.Error(codes.Internal, "Internal Server Error") } else if !deleted { return nil, status.Error(codes.InvalidArgument, "User not found") } s.consoleSessionCache.RemoveAll(id) return &emptypb.Empty{}, nil } Loading Loading @@ -154,17 +156,15 @@ func (s *ConsoleServer) dbListConsoleUsers(ctx context.Context) ([]*console.User return result, nil } func (s *ConsoleServer) dbDeleteConsoleUser(ctx context.Context, username string) (bool, error) { res, err := s.db.ExecContext(ctx, "DELETE FROM console_user WHERE username = $1", username) if err != nil { return false, err func (s *ConsoleServer) dbDeleteConsoleUser(ctx context.Context, username string) (bool, uuid.UUID, error) { var deletedID uuid.UUID if err := s.db.QueryRowContext(ctx, "DELETE FROM console_user WHERE username = $1 RETURNING id", username).Scan(&deletedID); err != nil { if err == sql.ErrNoRows { return false, uuid.Nil, nil } if n, err := res.RowsAffected(); err != nil { return false, err } else if n == 0 { return false, nil return false, uuid.Nil, err } return true, nil return true, deletedID, nil } func isValidPassword(pwd string) bool { Loading Loading
server/console.go +22 −7 Original line number Diff line number Diff line Loading @@ -175,7 +175,7 @@ func StartConsoleServer(logger *zap.Logger, startupLogger *zap.Logger, db *sql.D serverOpts := []grpc.ServerOption{ //grpc.StatsHandler(&ocgrpc.ServerHandler{IsPublicEndpoint: true}), grpc.MaxRecvMsgSize(int(config.GetConsole().MaxMessageSizeBytes)), grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config, consoleSessionCache)), grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config, consoleSessionCache, loginAttemptCache)), } grpcServer := grpc.NewServer(serverOpts...) Loading Loading @@ -438,7 +438,7 @@ func (s *ConsoleServer) Stop() { s.grpcServer.GracefulStop() } func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache SessionCache) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) { func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache SessionCache, loginAttmeptCache LoginAttemptCache) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { if info.FullMethod == "/nakama.console.Console/Authenticate" { // Skip authentication check for Login endpoint. Loading @@ -464,7 +464,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess return nil, status.Error(codes.Unauthenticated, "Console authentication required.") } if ctx, ok = checkAuth(ctx, config, auth[0], sessionCache); !ok { if ctx, ok = checkAuth(ctx, logger, config, auth[0], sessionCache, loginAttmeptCache); !ok { return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.") } role := ctx.Value(ctxConsoleRoleKey{}).(console.UserRole) Loading @@ -478,7 +478,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess } } func checkAuth(ctx context.Context, config Config, auth string, sessionCache SessionCache) (context.Context, bool) { func checkAuth(ctx context.Context, logger *zap.Logger, config Config, auth string, sessionCache SessionCache, loginAttemptCache LoginAttemptCache) (context.Context, bool) { const basicPrefix = "Basic " const bearerPrefix = "Bearer " Loading @@ -488,9 +488,24 @@ func checkAuth(ctx context.Context, config Config, auth string, sessionCache Ses if !ok { return ctx, false } if username != config.GetConsole().Username || password != config.GetConsole().Password { // Username and/or password do not match. ip, _ := extractClientAddressFromContext(logger, ctx) if !loginAttemptCache.Allow(username, ip) { return ctx, false } if username == config.GetConsole().Username { if password != config.GetConsole().Password { // Admin password does not match. if lockout, until := loginAttemptCache.Add(config.GetConsole().Username, ip); lockout != LockoutTypeNone { switch lockout { case LockoutTypeAccount: logger.Info(fmt.Sprintf("Console admin account locked until %v.", until)) case LockoutTypeIp: logger.Info(fmt.Sprintf("Console admin IP locked until %v.", until)) } } return ctx, false } } else { return ctx, false } Loading
server/console_storage_import.go +1 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,7 @@ func (s *ConsoleServer) importStorage(w http.ResponseWriter, r *http.Request) { } return } ctx, ok := checkAuth(r.Context(), s.config, auth, s.consoleSessionCache) ctx, ok := checkAuth(r.Context(), s.logger, s.config, auth, s.consoleSessionCache, s.loginAttemptCache) if !ok { w.WriteHeader(401) if _, err := w.Write([]byte("Console authentication invalid.")); err != nil { Loading
server/console_user.go +12 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package server import ( "bytes" "context" "database/sql" "encoding/json" "errors" "github.com/jackc/pgconn" Loading Loading @@ -118,13 +119,14 @@ func (s *ConsoleServer) dbInsertConsoleUser(ctx context.Context, in *console.Add } func (s *ConsoleServer) DeleteUser(ctx context.Context, in *console.Username) (*emptypb.Empty, error) { if deleted, err := s.dbDeleteConsoleUser(ctx, in.Username); err != nil { deleted, id, err := s.dbDeleteConsoleUser(ctx, in.Username) if err != nil { s.logger.Error("failed to delete console user", zap.Error(err), zap.String("username", in.Username)) return nil, status.Error(codes.Internal, "Internal Server Error") } else if !deleted { return nil, status.Error(codes.InvalidArgument, "User not found") } s.consoleSessionCache.RemoveAll(id) return &emptypb.Empty{}, nil } Loading Loading @@ -154,17 +156,15 @@ func (s *ConsoleServer) dbListConsoleUsers(ctx context.Context) ([]*console.User return result, nil } func (s *ConsoleServer) dbDeleteConsoleUser(ctx context.Context, username string) (bool, error) { res, err := s.db.ExecContext(ctx, "DELETE FROM console_user WHERE username = $1", username) if err != nil { return false, err func (s *ConsoleServer) dbDeleteConsoleUser(ctx context.Context, username string) (bool, uuid.UUID, error) { var deletedID uuid.UUID if err := s.db.QueryRowContext(ctx, "DELETE FROM console_user WHERE username = $1 RETURNING id", username).Scan(&deletedID); err != nil { if err == sql.ErrNoRows { return false, uuid.Nil, nil } if n, err := res.RowsAffected(); err != nil { return false, err } else if n == 0 { return false, nil return false, uuid.Nil, err } return true, nil return true, deletedID, nil } func isValidPassword(pwd string) bool { Loading