Skip to content

Commit 1b2eec9

Browse files
authored
feat(apiclient): add token save functionality (#3639)
1 parent bbc8d7f commit 1b2eec9

File tree

6 files changed

+36
-7
lines changed

6 files changed

+36
-7
lines changed

cmd/crowdsec-cli/clicapi/capi.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ func queryCAPIStatus(ctx context.Context, db *database.Client, hub *cwhub.Hub, c
198198
return false, false, err
199199
}
200200

201-
if err := db.SaveAPICToken(ctx, authResp.Token); err != nil {
201+
if err := db.SaveAPICToken(ctx, apiclient.TokenDBField, authResp.Token); err != nil {
202202
return false, false, err
203203
}
204204

pkg/apiclient/auth_jwt.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type JWTTransport struct {
3333
Transport http.RoundTripper
3434
UpdateScenario func(context.Context) ([]string, error)
3535
refreshTokenMutex sync.Mutex
36+
TokenSave TokenSave
3637
}
3738

3839
func (t *JWTTransport) refreshJwtToken() error {
@@ -134,6 +135,12 @@ func (t *JWTTransport) refreshJwtToken() error {
134135

135136
t.Token = response.Token
136137

138+
if t.TokenSave != nil {
139+
err = t.TokenSave(ctx, TokenDBField, t.Token)
140+
if err != nil {
141+
log.Errorf("unable to save token: %s", err)
142+
}
143+
}
137144
log.Debugf("token %s will expire on %s", t.Token, t.Expiration.String())
138145

139146
return nil

pkg/apiclient/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ var (
2626
lapiClient *ApiClient
2727
)
2828

29+
type TokenSave func(ctx context.Context, tokenKey string, token string) error
30+
2931
type ApiClient struct {
3032
/*The http client used to make requests*/
3133
client *http.Client
@@ -147,6 +149,7 @@ func NewClient(config *Config) (*ApiClient, error) {
147149
WithStatusCodeConfig(http.StatusServiceUnavailable, 5, true, false),
148150
WithStatusCodeConfig(http.StatusGatewayTimeout, 5, true, false),
149151
),
152+
TokenSave: config.TokenSave,
150153
}
151154

152155
transport, baseURL := createTransport(config.URL)

pkg/apiclient/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"github.com/go-openapi/strfmt"
88
)
99

10+
const TokenDBField = "apic_token"
11+
1012
type Config struct {
1113
MachineID string
1214
Password strfmt.Password
@@ -16,4 +18,5 @@ type Config struct {
1618
UserAgent string
1719
RegistrationToken string
1820
UpdateScenario func(context.Context) ([]string, error)
21+
TokenSave func(context.Context, string, string) error
1922
}

pkg/apiserver/apic.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ type apic struct {
7474
pullBlocklists bool
7575
pullCommunity bool
7676
shareSignals bool
77+
78+
TokenSave apiclient.TokenSave
7779
}
7880

7981
// randomDuration returns a duration value between d-delta and d+delta
@@ -228,6 +230,9 @@ func NewAPIC(ctx context.Context, config *csconfig.OnlineApiClientCfg, dbClient
228230
PapiURL: papiURL,
229231
VersionPrefix: "v3",
230232
UpdateScenario: ret.FetchScenariosListFromDB,
233+
TokenSave: func(ctx context.Context, tokenKey string, token string) error {
234+
return dbClient.SaveAPICToken(ctx, tokenKey, token)
235+
},
231236
})
232237
if err != nil {
233238
return nil, fmt.Errorf("while creating api client: %w", err)
@@ -277,7 +282,7 @@ func (a *apic) Authenticate(ctx context.Context, config *csconfig.OnlineApiClien
277282

278283
a.apiClient.GetClient().Transport.(*apiclient.JWTTransport).Token = authResp.Token
279284

280-
return a.dbClient.SaveAPICToken(ctx, authResp.Token)
285+
return a.dbClient.SaveAPICToken(ctx, apiclient.TokenDBField, authResp.Token)
281286
}
282287

283288
// keep track of all alerts in cache and push it to CAPI every PushInterval.

pkg/database/config.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import (
99
"github.com/pkg/errors"
1010
"github.com/sirupsen/logrus"
1111

12+
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
1213
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
1314
"github.com/crowdsecurity/crowdsec/pkg/database/ent/configitem"
1415
)
1516

16-
const apicTokenKey = "apic_token"
17-
1817
func (c *Client) GetConfigItem(ctx context.Context, key string) (string, error) {
1918
result, err := c.Ent.ConfigItem.Query().Where(configitem.NameEQ(key)).First(ctx)
2019

@@ -53,7 +52,7 @@ func (c *Client) SetConfigItem(ctx context.Context, key string, value string) er
5352
// - it is a properly formatted JWT with an "exp" claim,
5453
// - it is not expired or near expiry.
5554
func (c *Client) LoadAPICToken(ctx context.Context, logger logrus.FieldLogger) (string, time.Time, bool) {
56-
token, err := c.GetConfigItem(ctx, apicTokenKey)
55+
token, err := c.GetConfigItem(ctx, apiclient.TokenDBField) // TokenKey is a constant string representing the key for the token in the database
5756
if err != nil {
5857
logger.Debugf("error fetching token from DB: %s", err)
5958
return "", time.Time{}, false
@@ -78,6 +77,18 @@ func (c *Client) LoadAPICToken(ctx context.Context, logger logrus.FieldLogger) (
7877
return "", time.Time{}, false
7978
}
8079

80+
iatFloat, ok := claims["iat"].(float64)
81+
if !ok {
82+
logger.Debug("token missing 'iat' claim")
83+
return "", time.Time{}, false
84+
}
85+
86+
iat := time.Unix(int64(iatFloat), 0)
87+
if time.Now().UTC().After(iat.Add(1 * time.Minute)) {
88+
logger.Debug("token is more than 1 minute old, not using it")
89+
return "", time.Time{}, false
90+
}
91+
8192
expFloat, ok := claims["exp"].(float64)
8293
if !ok {
8394
logger.Debug("token missing 'exp' claim")
@@ -94,8 +105,8 @@ func (c *Client) LoadAPICToken(ctx context.Context, logger logrus.FieldLogger) (
94105
}
95106

96107
// SaveAPICToken stores the given JWT token in the local database under the appropriate config item.
97-
func (c *Client) SaveAPICToken(ctx context.Context, token string) error {
98-
if err := c.SetConfigItem(ctx, apicTokenKey, token); err != nil {
108+
func (c *Client) SaveAPICToken(ctx context.Context, tokenKey string, token string) error {
109+
if err := c.SetConfigItem(ctx, tokenKey, token); err != nil {
99110
return fmt.Errorf("saving token to db: %w", err)
100111
}
101112

0 commit comments

Comments
 (0)