Skip to content

Commit d1e894f

Browse files
authored
Console session handling improvements. (#979)
1 parent d225d20 commit d1e894f

File tree

3 files changed

+35
-20
lines changed

3 files changed

+35
-20
lines changed

server/console.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func StartConsoleServer(logger *zap.Logger, startupLogger *zap.Logger, db *sql.D
175175
serverOpts := []grpc.ServerOption{
176176
//grpc.StatsHandler(&ocgrpc.ServerHandler{IsPublicEndpoint: true}),
177177
grpc.MaxRecvMsgSize(int(config.GetConsole().MaxMessageSizeBytes)),
178-
grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config, consoleSessionCache)),
178+
grpc.UnaryInterceptor(consoleInterceptorFunc(logger, config, consoleSessionCache, loginAttemptCache)),
179179
}
180180
grpcServer := grpc.NewServer(serverOpts...)
181181

@@ -438,7 +438,7 @@ func (s *ConsoleServer) Stop() {
438438
s.grpcServer.GracefulStop()
439439
}
440440

441-
func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache SessionCache) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) {
441+
func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache SessionCache, loginAttmeptCache LoginAttemptCache) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) {
442442
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
443443
if info.FullMethod == "/nakama.console.Console/Authenticate" {
444444
// Skip authentication check for Login endpoint.
@@ -464,7 +464,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess
464464
return nil, status.Error(codes.Unauthenticated, "Console authentication required.")
465465
}
466466

467-
if ctx, ok = checkAuth(ctx, config, auth[0], sessionCache); !ok {
467+
if ctx, ok = checkAuth(ctx, logger, config, auth[0], sessionCache, loginAttmeptCache); !ok {
468468
return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.")
469469
}
470470
role := ctx.Value(ctxConsoleRoleKey{}).(console.UserRole)
@@ -478,7 +478,7 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config, sessionCache Sess
478478
}
479479
}
480480

481-
func checkAuth(ctx context.Context, config Config, auth string, sessionCache SessionCache) (context.Context, bool) {
481+
func checkAuth(ctx context.Context, logger *zap.Logger, config Config, auth string, sessionCache SessionCache, loginAttemptCache LoginAttemptCache) (context.Context, bool) {
482482
const basicPrefix = "Basic "
483483
const bearerPrefix = "Bearer "
484484

@@ -488,9 +488,24 @@ func checkAuth(ctx context.Context, config Config, auth string, sessionCache Ses
488488
if !ok {
489489
return ctx, false
490490
}
491-
492-
if username != config.GetConsole().Username || password != config.GetConsole().Password {
493-
// Username and/or password do not match.
491+
ip, _ := extractClientAddressFromContext(logger, ctx)
492+
if !loginAttemptCache.Allow(username, ip) {
493+
return ctx, false
494+
}
495+
if username == config.GetConsole().Username {
496+
if password != config.GetConsole().Password {
497+
// Admin password does not match.
498+
if lockout, until := loginAttemptCache.Add(config.GetConsole().Username, ip); lockout != LockoutTypeNone {
499+
switch lockout {
500+
case LockoutTypeAccount:
501+
logger.Info(fmt.Sprintf("Console admin account locked until %v.", until))
502+
case LockoutTypeIp:
503+
logger.Info(fmt.Sprintf("Console admin IP locked until %v.", until))
504+
}
505+
}
506+
return ctx, false
507+
}
508+
} else {
494509
return ctx, false
495510
}
496511

server/console_storage_import.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func (s *ConsoleServer) importStorage(w http.ResponseWriter, r *http.Request) {
5454
}
5555
return
5656
}
57-
ctx, ok := checkAuth(r.Context(), s.config, auth, s.consoleSessionCache)
57+
ctx, ok := checkAuth(r.Context(), s.logger, s.config, auth, s.consoleSessionCache, s.loginAttemptCache)
5858
if !ok {
5959
w.WriteHeader(401)
6060
if _, err := w.Write([]byte("Console authentication invalid.")); err != nil {

server/console_user.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package server
1717
import (
1818
"bytes"
1919
"context"
20+
"database/sql"
2021
"encoding/json"
2122
"errors"
2223
"github.com/jackc/pgconn"
@@ -118,13 +119,14 @@ func (s *ConsoleServer) dbInsertConsoleUser(ctx context.Context, in *console.Add
118119
}
119120

120121
func (s *ConsoleServer) DeleteUser(ctx context.Context, in *console.Username) (*emptypb.Empty, error) {
121-
122-
if deleted, err := s.dbDeleteConsoleUser(ctx, in.Username); err != nil {
122+
deleted, id, err := s.dbDeleteConsoleUser(ctx, in.Username)
123+
if err != nil {
123124
s.logger.Error("failed to delete console user", zap.Error(err), zap.String("username", in.Username))
124125
return nil, status.Error(codes.Internal, "Internal Server Error")
125126
} else if !deleted {
126127
return nil, status.Error(codes.InvalidArgument, "User not found")
127128
}
129+
s.consoleSessionCache.RemoveAll(id)
128130

129131
return &emptypb.Empty{}, nil
130132
}
@@ -154,17 +156,15 @@ func (s *ConsoleServer) dbListConsoleUsers(ctx context.Context) ([]*console.User
154156
return result, nil
155157
}
156158

157-
func (s *ConsoleServer) dbDeleteConsoleUser(ctx context.Context, username string) (bool, error) {
158-
res, err := s.db.ExecContext(ctx, "DELETE FROM console_user WHERE username = $1", username)
159-
if err != nil {
160-
return false, err
161-
}
162-
if n, err := res.RowsAffected(); err != nil {
163-
return false, err
164-
} else if n == 0 {
165-
return false, nil
159+
func (s *ConsoleServer) dbDeleteConsoleUser(ctx context.Context, username string) (bool, uuid.UUID, error) {
160+
var deletedID uuid.UUID
161+
if err := s.db.QueryRowContext(ctx, "DELETE FROM console_user WHERE username = $1 RETURNING id", username).Scan(&deletedID); err != nil {
162+
if err == sql.ErrNoRows {
163+
return false, uuid.Nil, nil
164+
}
165+
return false, uuid.Nil, err
166166
}
167-
return true, nil
167+
return true, deletedID, nil
168168
}
169169

170170
func isValidPassword(pwd string) bool {

0 commit comments

Comments
 (0)