Skip to content

Commit 961e283

Browse files
committed
Adding pkgs for teams and users
1 parent 4cee760 commit 961e283

10 files changed

Lines changed: 2089 additions & 5 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ tmp
1717
# Logs
1818
*.log
1919

20+
# Tests
21+
coverage.out
22+
2023
# Configuration files except Docker env
2124
*.json
2225
*.yaml

backend/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,9 @@ endif
5555
# Run linter
5656
lint:
5757
golangci-lint run
58+
59+
# Test with coverage
60+
test:
61+
go test ./pkg/version -v -cover
62+
go test ./pkg/users -v -cover
63+
go test ./pkg/teams -v -cover

backend/cmd/api/handlers/handlers.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package handlers
33
import (
44
"github.com/jmpsec/mapctf/pkg/cache"
55
"github.com/jmpsec/mapctf/pkg/config"
6+
"github.com/jmpsec/mapctf/pkg/teams"
7+
"github.com/jmpsec/mapctf/pkg/users"
68
"github.com/rs/zerolog"
79
"gopkg.in/natefinch/lumberjack.v2"
810
"gorm.io/gorm"
@@ -25,6 +27,8 @@ const (
2527
type HandlersAPI struct {
2628
DB *gorm.DB
2729
RedisCache *cache.RedisManager
30+
Teams *teams.TeamManager
31+
Users *users.UserManager
2832
Config config.MapCTFConfiguration
2933
DebugHTTP *zerolog.Logger
3034
}
@@ -61,6 +65,18 @@ func WithConfig(cfg config.MapCTFConfiguration) HandlersOption {
6165
}
6266
}
6367

68+
func WithTeams(teams *teams.TeamManager) HandlersOption {
69+
return func(h *HandlersAPI) {
70+
h.Teams = teams
71+
}
72+
}
73+
74+
func WithUsers(users *users.UserManager) HandlersOption {
75+
return func(h *HandlersAPI) {
76+
h.Users = users
77+
}
78+
}
79+
6480
// createDebugHTTP to initialize the debug HTTP logger
6581
func createDebugHTTP(filename string) (*zerolog.Logger, error) {
6682
zerolog.TimeFieldFormat = LoggerTimeFormat

backend/cmd/api/main.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/jmpsec/mapctf/pkg/backend"
1616
"github.com/jmpsec/mapctf/pkg/cache"
1717
"github.com/jmpsec/mapctf/pkg/config"
18+
"github.com/jmpsec/mapctf/pkg/teams"
19+
"github.com/jmpsec/mapctf/pkg/users"
1820
"github.com/jmpsec/mapctf/pkg/version"
1921
"github.com/rs/zerolog"
2022
"github.com/rs/zerolog/log"
@@ -35,6 +37,8 @@ const (
3537
LoggerTimeFormat string = "2006-01-02T15:04:05.999Z07:00"
3638
// Default path used when generating example configs
3739
defaultExampleConfigPath = "config/mapctf.example.yaml"
40+
// Default path for service configuration file
41+
defaultConfigPath = "config/mapctf.yaml"
3842
)
3943

4044
// Paths
@@ -64,6 +68,8 @@ const (
6468
apiStatsPath = "/stats"
6569
// API teams path
6670
apiTeamsPath = "/teams"
71+
// API users path
72+
apiUsersPath = "/users"
6773
// API challenges path
6874
apiChallengesPath = "/challenges"
6975
)
@@ -109,7 +115,7 @@ func configFileFromCommand(cmd *cli.Command) string {
109115
if flagParams.ConfigFile != "" {
110116
return flagParams.ConfigFile
111117
}
112-
return "config/mapctf.yaml"
118+
return defaultConfigPath
113119
}
114120

115121
// Initialization code
@@ -135,7 +141,7 @@ DELETE /api/admin/challenges/:id - Delete challenge
135141

136142
// Let's go!
137143
func mapCTFService() {
138-
// ////////////////////////////// Backend
144+
// Backend
139145
log.Info().Msg("Initializing backend...")
140146
for {
141147
db, err = backend.CreateDBManager(flagParams.ConfigValues.DB)
@@ -152,7 +158,7 @@ func mapCTFService() {
152158
log.Debug().Msgf("Backend NOT ready! Retrying in %d seconds...\n", flagParams.ConfigValues.DB.ConnRetry)
153159
time.Sleep(time.Duration(flagParams.ConfigValues.DB.ConnRetry) * time.Second)
154160
}
155-
// ////////////////////////////// Cache
161+
// Cache
156162
log.Info().Msg("Initializing cache...")
157163
for {
158164
redis, err = cache.CreateRedisManager(flagParams.ConfigValues.Redis)
@@ -169,15 +175,23 @@ func mapCTFService() {
169175
log.Debug().Msgf("Cache NOT ready! Retrying in %d seconds...\n", flagParams.ConfigValues.Redis.ConnRetry)
170176
time.Sleep(time.Duration(flagParams.ConfigValues.Redis.ConnRetry) * time.Second)
171177
}
172-
// ////////////////////////////// Handlers
178+
// Team Manager
179+
log.Info().Msg("Initialize teams")
180+
teamsMgr := teams.CreateTeams(db.Conn)
181+
// User Manager
182+
log.Info().Msg("Initialize users")
183+
usersMgr := users.CreateUserManager(db.Conn)
184+
// Handlers
173185
log.Info().Msg("Initializing handlers")
174186
handlersCTF := handlers.CreateHandlersAPI(
175187
handlers.WithDB(db.Conn),
176188
handlers.WithRedisCache(redis),
177189
handlers.WithConfig(flagParams.ConfigValues),
190+
handlers.WithTeams(teamsMgr),
191+
handlers.WithUsers(usersMgr), // User manager to be added
178192
handlers.WithDebugHTTP(&flagParams.ConfigValues.DebugHTTP),
179193
)
180-
// ////////////////////////////// Router
194+
// Router
181195
log.Info().Msg("Initializing router")
182196
// Create router for API
183197
muxAPI := http.NewServeMux()

backend/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ require (
4343
github.com/pmezard/go-difflib v1.0.0 // indirect
4444
github.com/rs/zerolog v1.34.0
4545
github.com/spf13/viper v1.21.0
46+
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15
4647
github.com/urfave/cli/v3 v3.6.2
4748
gopkg.in/natefinch/lumberjack.v2 v2.2.1
4849
gopkg.in/yaml.v3 v3.0.1

backend/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
9595
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
9696
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
9797
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
98+
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI=
99+
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
98100
github.com/urfave/cli/v3 v3.5.0 h1:qCuFMmdayTF3zmjG8TSsoBzrDqszNrklYg2x3g4MSgw=
99101
github.com/urfave/cli/v3 v3.5.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
100102
github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=

backend/pkg/teams/teams.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package teams
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/rs/zerolog/log"
8+
"gorm.io/gorm"
9+
)
10+
11+
// TeamManager to handle all teams of the platform
12+
type TeamManager struct {
13+
DB *gorm.DB
14+
}
15+
16+
// PlatformTeam to hold all teams of the platform
17+
type PlatformTeam struct {
18+
gorm.Model
19+
Name string `gorm:"index"`
20+
Logo string
21+
Points int
22+
LastScore time.Time
23+
Protected bool
24+
Visible bool
25+
Active bool
26+
EntID uint
27+
}
28+
29+
// TeamMembership to link users to teams
30+
type TeamMembership struct {
31+
gorm.Model
32+
TeamID uint `gorm:"index"`
33+
UserID uint `gorm:"index"`
34+
EntID uint
35+
AssignedBy uint
36+
}
37+
38+
// TeamScore to hold all team scores over time
39+
type TeamScore struct {
40+
gorm.Model
41+
TeamID uint `gorm:"index"`
42+
Points int
43+
EntID uint
44+
ScoredBy uint
45+
}
46+
47+
// CreateTeams to initialize the teams struct and its tables
48+
func CreateTeams(backend *gorm.DB) *TeamManager {
49+
t := &TeamManager{
50+
DB: backend,
51+
}
52+
// table platform_teams
53+
if err := backend.AutoMigrate(&PlatformTeam{}); err != nil {
54+
log.Fatal().Msgf("Failed to AutoMigrate table (platform_teams): %v", err)
55+
}
56+
// table team_memberships
57+
if err := backend.AutoMigrate(&TeamMembership{}); err != nil {
58+
log.Fatal().Msgf("Failed to AutoMigrate table (team_memberships): %v", err)
59+
}
60+
// table team_scores
61+
if err := backend.AutoMigrate(&TeamScore{}); err != nil {
62+
log.Fatal().Msgf("Failed to AutoMigrate table (team_scores): %v", err)
63+
}
64+
return t
65+
}
66+
67+
// Create new team
68+
func (m *TeamManager) Create(team PlatformTeam) error {
69+
if err := m.DB.Create(&team).Error; err != nil {
70+
return fmt.Errorf("Create PlatformTeam %w", err)
71+
}
72+
return nil
73+
}
74+
75+
// Exists checks if user exists
76+
func (m *TeamManager) Exists(name string) bool {
77+
var results int64
78+
m.DB.Model(&PlatformTeam{}).Where("name = ?", name).Count(&results)
79+
return (results > 0)
80+
}
81+
82+
// Get user by username including service users
83+
func (m *TeamManager) Get(name string) (PlatformTeam, error) {
84+
var team PlatformTeam
85+
if err := m.DB.Where("name = ?", name).First(&team).Error; err != nil {
86+
return team, err
87+
}
88+
return team, nil
89+
}
90+
91+
// Get user by username and by tenant ID, including service users
92+
func (m *TeamManager) GetByTenantID(name string, tenantID uint) (PlatformTeam, error) {
93+
var team PlatformTeam
94+
if err := m.DB.Where("name = ? AND ent_id = ?", name, tenantID).First(&team).Error; err != nil {
95+
return team, err
96+
}
97+
return team, nil
98+
}
99+
100+
// ExistsGet checks if user exists and returns the user
101+
func (m *TeamManager) ExistsGet(name string) (bool, PlatformTeam) {
102+
team, err := m.Get(name)
103+
if err != nil {
104+
return false, PlatformTeam{}
105+
}
106+
return true, team
107+
}
108+
109+
// ExistsGetByTenantID checks if user exists and returns the user
110+
func (m *TeamManager) ExistsGetByTenantID(name string, tenantID uint) (bool, PlatformTeam) {
111+
team, err := m.GetByTenantID(name, tenantID)
112+
if err != nil {
113+
return false, PlatformTeam{}
114+
}
115+
return true, team
116+
}
117+
118+
// New empty user
119+
func (m *TeamManager) New(name, logo, email string, protected, visible bool, eID uint) (PlatformTeam, error) {
120+
if !m.Exists(name) {
121+
return PlatformTeam{
122+
Name: name,
123+
Logo: logo,
124+
Protected: protected,
125+
Visible: visible,
126+
Active: true,
127+
EntID: eID,
128+
}, nil
129+
}
130+
return PlatformTeam{}, fmt.Errorf("%s already exists", name)
131+
}

0 commit comments

Comments
 (0)