From 57c2c3f95947142c430f5721a2ad3624be3db143 Mon Sep 17 00:00:00 2001 From: Fellipe Leonardo <112525075+neoRandom@users.noreply.github.com> Date: Fri, 21 Aug 2026 00:39:30 -0300 Subject: [PATCH 1/2] chore: remove unnecessary interface method --- internal/application/usecase/authentication.go | 6 ------ internal/core/port/driver.go | 3 --- 2 files changed, 9 deletions(-) diff --git a/internal/application/usecase/authentication.go b/internal/application/usecase/authentication.go index 21175c1..fa72965 100644 --- a/internal/application/usecase/authentication.go +++ b/internal/application/usecase/authentication.go @@ -99,9 +99,3 @@ func (uc *Authentication) LoginPlayer( Player: player, }, nil } - -func (uc *Authentication) ValidateSession( - ctx context.Context, token string, -) (database.Player, error) { - return database.Player{}, nil -} diff --git a/internal/core/port/driver.go b/internal/core/port/driver.go index 2d03ff9..3b98a7d 100644 --- a/internal/core/port/driver.go +++ b/internal/core/port/driver.go @@ -42,9 +42,6 @@ type Authentication interface { LoginPlayer( ctx context.Context, username string, password string, ) (model.AuthResult, error) - ValidateSession( - ctx context.Context, token string, - ) (database.Player, error) } type DataFetching interface { From 1da4cd39860920c22215de5b0079dddbd5a14490 Mon Sep 17 00:00:00 2001 From: Fellipe Leonardo <112525075+neoRandom@users.noreply.github.com> Date: Fri, 21 Aug 2026 00:54:13 -0300 Subject: [PATCH 2/2] feat: implement method to check directly if player has access to game --- README.md | 25 ++++----- database/queries/access.sql | 6 +++ .../application/usecase/access_management.go | 24 +++++++++ internal/core/port/driver.go | 23 ++++---- .../infrastructure/database/access.sql.go | 19 +++++++ .../infrastructure/driver/cliadapter/cli.go | 52 +++++++++++++------ 6 files changed, 110 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 931b09c..f4027d7 100644 --- a/README.md +++ b/README.md @@ -17,25 +17,26 @@ Usage: keepitup [args...] Nouns: - game add + game add update delete - access grant + access grant revoke - player add - rename - passwd + check + player add + rename + passwd passwd-force - delete + delete auth validate-passwd - hash-passwd - check-passwd - fetch games - shared + hash-passwd + check-passwd + fetch games + shared interactions - command save + command save resume - pause + pause ``` ## HTTP API diff --git a/database/queries/access.sql b/database/queries/access.sql index 1147955..d724b09 100644 --- a/database/queries/access.sql +++ b/database/queries/access.sql @@ -6,3 +6,9 @@ RETURNING game_id, player_id; -- name: RevokePlayerAccess :exec DELETE FROM access WHERE game_id = ? AND player_id = ?; + +-- name: CheckPlayerAccess :one +SELECT EXISTS ( + SELECT 1 FROM access + WHERE game_id = ? AND player_id = ? +); diff --git a/internal/application/usecase/access_management.go b/internal/application/usecase/access_management.go index 9799e38..3a21fdb 100644 --- a/internal/application/usecase/access_management.go +++ b/internal/application/usecase/access_management.go @@ -64,3 +64,27 @@ func (uc *AccessManagement) RevokePlayerAccess( }, ) } + +func (uc *AccessManagement) CheckPlayerAccess( + ctx context.Context, gameId, playerId int64, +) (bool, error) { + if uc.q == nil { + return false, errors.New("database queries are not initialized") + } + + if gameId < 1 { + return false, fmt.Errorf("invalid game ID: %d", gameId) + } + + if playerId < 1 { + return false, fmt.Errorf("invalid player ID: %d", playerId) + } + + return uc.q.CheckPlayerAccess( + ctx, + database.CheckPlayerAccessParams{ + GameID: gameId, + PlayerID: playerId, + }, + ) +} diff --git a/internal/core/port/driver.go b/internal/core/port/driver.go index 3b98a7d..60b89d1 100644 --- a/internal/core/port/driver.go +++ b/internal/core/port/driver.go @@ -13,19 +13,20 @@ type GameManagement interface { } type AccessManagement interface { - GrantPlayerAccess(ctx context.Context, gameId int64, playerId int64) error - RevokePlayerAccess(ctx context.Context, gameId int64, playerId int64) error + GrantPlayerAccess(ctx context.Context, gameId, playerId int64) error + RevokePlayerAccess(ctx context.Context, gameId, playerId int64) error + CheckPlayerAccess(ctx context.Context, gameId, playerId int64) (bool, error) } type PlayerManagement interface { AddPlayer( - ctx context.Context, name string, username string, password string, + ctx context.Context, name, username, password string, ) (database.Player, error) UpdatePlayerName( ctx context.Context, id int64, name string, ) error UpdatePlayerPassword( - ctx context.Context, id int64, currentPassword string, newPassword string, + ctx context.Context, id int64, currentPassword, newPassword string, ) error UpdatePlayerPasswordForce( ctx context.Context, id int64, password string, @@ -37,10 +38,10 @@ type PlayerManagement interface { type Authentication interface { CheckPlayerPassword( - ctx context.Context, username string, password string, + ctx context.Context, username, password string, ) (bool, error) LoginPlayer( - ctx context.Context, username string, password string, + ctx context.Context, username, password string, ) (model.AuthResult, error) } @@ -52,7 +53,7 @@ type DataFetching interface { ctx context.Context, gameId int64, ) (*model.SharedData, error) ListInteractions( - ctx context.Context, gameId int64, limit int64, + ctx context.Context, gameId, limit int64, ) ([]database.Interaction, error) // TODO: List Player Interactions // TODO: First Interaction @@ -60,9 +61,7 @@ type DataFetching interface { } type GameCommands interface { - SaveGame( - ctx context.Context, gameId int64, playerId int64, duration int64, - ) error - ResumeGame(ctx context.Context, gameId int64, playerId int64) error - PauseGame(ctx context.Context, gameId int64, playerId int64) error + SaveGame(ctx context.Context, gameId, playerId, duration int64) error + ResumeGame(ctx context.Context, gameId, playerId int64) error + PauseGame(ctx context.Context, gameId, playerId int64) error } diff --git a/internal/infrastructure/database/access.sql.go b/internal/infrastructure/database/access.sql.go index 5366fdc..1bad552 100644 --- a/internal/infrastructure/database/access.sql.go +++ b/internal/infrastructure/database/access.sql.go @@ -9,6 +9,25 @@ import ( "context" ) +const checkPlayerAccess = `-- name: CheckPlayerAccess :one +SELECT EXISTS ( + SELECT 1 FROM access + WHERE game_id = ? AND player_id = ? +) +` + +type CheckPlayerAccessParams struct { + GameID int64 + PlayerID int64 +} + +func (q *Queries) CheckPlayerAccess(ctx context.Context, arg CheckPlayerAccessParams) (bool, error) { + row := q.db.QueryRowContext(ctx, checkPlayerAccess, arg.GameID, arg.PlayerID) + var exists bool + err := row.Scan(&exists) + return exists, err +} + const grantPlayerAccess = `-- name: GrantPlayerAccess :one INSERT INTO access (game_id, player_id) VALUES (?, ?) diff --git a/internal/infrastructure/driver/cliadapter/cli.go b/internal/infrastructure/driver/cliadapter/cli.go index 9f54602..d234c47 100644 --- a/internal/infrastructure/driver/cliadapter/cli.go +++ b/internal/infrastructure/driver/cliadapter/cli.go @@ -21,29 +21,27 @@ var ( const usage = `keep-it-up --- system management CLI -Usage: - keepitup [args...] - Nouns: - game add + game add update delete - access grant + access grant revoke - player add - rename - passwd + check + player add + rename + passwd passwd-force - delete + delete auth validate-passwd - hash-passwd - check-passwd - fetch games - shared + hash-passwd + check-passwd + fetch games + shared interactions - command save + command save resume - pause + pause ` // Deps groups the driver ports and I/O streams the CLI needs. It is a @@ -215,6 +213,30 @@ func (c *CLI) runAccess(ctx context.Context, args []string) error { fmt.Fprintf(c.d.Stdout, "player %d access to game %d revoked\n", playerID, gameID) return nil + case "check": + if len(rest) != 2 { + return wrongArgs("access check", "access check ") + } + gameID, err := parseID(rest[0]) + if err != nil { + return fmt.Errorf("access check: %w", err) + } + playerID, err := parseID(rest[1]) + if err != nil { + return fmt.Errorf("access check: %w", err) + } + + access, err := c.d.Access.CheckPlayerAccess(ctx, gameID, playerID) + if err != nil { + return fmt.Errorf("access check: %w", err) + } + fmt.Fprintf( + c.d.Stdout, + "player %d access to game %d: %v\n", + playerID, gameID, access, + ) + return nil + default: return fmt.Errorf("access %s: %w", verb, ErrUnknownSubcommand) }