11package security
22
33import (
4+ "context"
45 "fmt"
56 "net/http"
67 "testing"
8+ "time"
79
810 registry_token "github.com/docker/distribution/registry/auth/token"
11+ "github.com/golang-jwt/jwt/v5"
912 "github.com/stretchr/testify/assert"
1013 "github.com/stretchr/testify/require"
1114
15+ project_ctl "github.com/goharbor/harbor/src/controller/project"
1216 "github.com/goharbor/harbor/src/core/service/token"
17+ "github.com/goharbor/harbor/src/lib"
1318 "github.com/goharbor/harbor/src/lib/config"
19+ "github.com/goharbor/harbor/src/lib/log"
1420 "github.com/goharbor/harbor/src/lib/orm"
21+ proModels "github.com/goharbor/harbor/src/pkg/project/models"
22+ v2 "github.com/goharbor/harbor/src/pkg/token/claims/v2"
23+ projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
24+ "github.com/goharbor/harbor/src/testing/mock"
1525)
1626
1727func TestGenerate (t * testing.T ) {
@@ -34,3 +44,60 @@ func TestGenerate(t *testing.T) {
3444 req4 .Header .Set ("Authorization" , fmt .Sprintf ("Bearer %s" , mt2 .Token ))
3545 assert .NotNil (t , vt .Generate (req4 ))
3646}
47+
48+ func makeClaimsWithIAT (iat time.Time ) * v2TokenClaims {
49+ return & v2TokenClaims {
50+ Claims : v2.Claims {
51+ RegisteredClaims : jwt.RegisteredClaims {
52+ IssuedAt : jwt .NewNumericDate (iat ),
53+ },
54+ },
55+ }
56+ }
57+
58+ func TestTokenIssuedAfterProjectCreation (t * testing.T ) {
59+ logger := log .DefaultLogger ()
60+ projectCreated := time .Date (2025 , 1 , 1 , 0 , 0 , 0 , 0 , time .UTC )
61+ before := time .Date (2024 , 1 , 1 , 0 , 0 , 0 , 0 , time .UTC )
62+ after := time .Date (2025 , 6 , 1 , 0 , 0 , 0 , 0 , time .UTC )
63+
64+ proj := & proModels.Project {Name : "myproject" , CreationTime : projectCreated }
65+
66+ tests := []struct {
67+ name string
68+ projectName string
69+ iat time.Time
70+ project * proModels.Project
71+ projErr error
72+ allowed bool
73+ }{
74+ {"after creation - allowed" , "myproject" , after , proj , nil , true },
75+ {"before creation - rejected" , "myproject" , before , proj , nil , false },
76+ {"exact creation time - allowed" , "myproject" , projectCreated , proj , nil , true },
77+ {"within leeway window - allowed" , "myproject" , projectCreated .Add (- 30 * time .Second ), proj , nil , true },
78+ {"just outside leeway - rejected" , "myproject" , projectCreated .Add (- 61 * time .Second ), proj , nil , false },
79+ {"no project in context - skipped" , "" , after , nil , nil , true },
80+ {"project lookup error - rejected" , "myproject" , after , nil , fmt .Errorf ("not found" ), false },
81+ }
82+
83+ for _ , tt := range tests {
84+ t .Run (tt .name , func (t * testing.T ) {
85+ origCtl := project_ctl .Ctl
86+ defer func () { project_ctl .Ctl = origCtl }()
87+
88+ mockCtl := & projecttesting.Controller {}
89+ project_ctl .Ctl = mockCtl
90+ if tt .project != nil || tt .projErr != nil {
91+ mock .OnAnything (mockCtl , "GetByName" ).Return (tt .project , tt .projErr )
92+ }
93+
94+ ctx := context .Background ()
95+ if tt .projectName != "" {
96+ ctx = lib .WithArtifactInfo (ctx , lib.ArtifactInfo {ProjectName : tt .projectName })
97+ }
98+
99+ result := tokenIssuedAfterProjectCreation (ctx , logger , makeClaimsWithIAT (tt .iat ))
100+ assert .Equal (t , tt .allowed , result )
101+ })
102+ }
103+ }
0 commit comments