Skip to content

Commit b7eeb43

Browse files
authored
feat: Add ip_address to API keys (#2580)
Fixes #2561.
1 parent caf9c41 commit b7eeb43

File tree

13 files changed

+144
-49
lines changed

13 files changed

+144
-49
lines changed

coderd/database/databasefake/databasefake.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,7 @@ func (q *fakeQuerier) InsertAPIKey(_ context.Context, arg database.InsertAPIKeyP
13821382
ID: arg.ID,
13831383
LifetimeSeconds: arg.LifetimeSeconds,
13841384
HashedSecret: arg.HashedSecret,
1385+
IPAddress: arg.IPAddress,
13851386
UserID: arg.UserID,
13861387
ExpiresAt: arg.ExpiresAt,
13871388
CreatedAt: arg.CreatedAt,
@@ -1802,6 +1803,7 @@ func (q *fakeQuerier) UpdateAPIKeyByID(_ context.Context, arg database.UpdateAPI
18021803
}
18031804
apiKey.LastUsed = arg.LastUsed
18041805
apiKey.ExpiresAt = arg.ExpiresAt
1806+
apiKey.IPAddress = arg.IPAddress
18051807
apiKey.OAuthAccessToken = arg.OAuthAccessToken
18061808
apiKey.OAuthRefreshToken = arg.OAuthRefreshToken
18071809
apiKey.OAuthExpiry = arg.OAuthExpiry

coderd/database/dump.sql

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE ONLY api_keys
2+
DROP COLUMN IF EXISTS ip_address;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE ONLY api_keys
2+
ADD COLUMN IF NOT EXISTS ip_address inet NOT NULL DEFAULT '0.0.0.0';

coderd/database/models.go

Lines changed: 14 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 34 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/apikeys.sql

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ INSERT INTO
1717
id,
1818
lifetime_seconds,
1919
hashed_secret,
20+
ip_address,
2021
user_id,
2122
last_used,
2223
expires_at,
@@ -35,17 +36,18 @@ VALUES
3536
WHEN 0 THEN 86400
3637
ELSE @lifetime_seconds::bigint
3738
END
38-
, @hashed_secret, @user_id, @last_used, @expires_at, @created_at, @updated_at, @login_type, @oauth_access_token, @oauth_refresh_token, @oauth_id_token, @oauth_expiry) RETURNING *;
39+
, @hashed_secret, @ip_address, @user_id, @last_used, @expires_at, @created_at, @updated_at, @login_type, @oauth_access_token, @oauth_refresh_token, @oauth_id_token, @oauth_expiry) RETURNING *;
3940

4041
-- name: UpdateAPIKeyByID :exec
4142
UPDATE
4243
api_keys
4344
SET
4445
last_used = $2,
4546
expires_at = $3,
46-
oauth_access_token = $4,
47-
oauth_refresh_token = $5,
48-
oauth_expiry = $6
47+
ip_address = $4,
48+
oauth_access_token = $5,
49+
oauth_refresh_token = $6,
50+
oauth_expiry = $7
4951
WHERE
5052
id = $1;
5153

coderd/database/sqlc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ rename:
2929
userstatus: UserStatus
3030
gitsshkey: GitSSHKey
3131
rbac_roles: RBACRoles
32+
ip_address: IPAddress

coderd/httpmw/apikey.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ import (
77
"database/sql"
88
"errors"
99
"fmt"
10+
"net"
1011
"net/http"
1112
"strings"
1213
"time"
1314

1415
"golang.org/x/oauth2"
1516

17+
"github.com/tabbed/pqtype"
18+
1619
"github.com/coder/coder/coderd/database"
1720
"github.com/coder/coder/coderd/httpapi"
1821
)
@@ -164,6 +167,17 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
164167
// Only update LastUsed once an hour to prevent database spam.
165168
if now.Sub(key.LastUsed) > time.Hour {
166169
key.LastUsed = now
170+
remoteIP := net.ParseIP(r.RemoteAddr)
171+
if remoteIP == nil {
172+
remoteIP = net.IPv4(0, 0, 0, 0)
173+
}
174+
key.IPAddress = pqtype.Inet{
175+
IPNet: net.IPNet{
176+
IP: remoteIP,
177+
Mask: remoteIP.DefaultMask(),
178+
},
179+
Valid: true,
180+
}
167181
changed = true
168182
}
169183
// Only update the ExpiresAt once an hour to prevent database spam.
@@ -178,6 +192,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
178192
ID: key.ID,
179193
LastUsed: key.LastUsed,
180194
ExpiresAt: key.ExpiresAt,
195+
IPAddress: key.IPAddress,
181196
OAuthAccessToken: key.OAuthAccessToken,
182197
OAuthRefreshToken: key.OAuthRefreshToken,
183198
OAuthExpiry: key.OAuthExpiry,

coderd/httpmw/apikey_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,41 @@ func TestAPIKey(t *testing.T) {
402402
require.Equal(t, token.Expiry, gotAPIKey.ExpiresAt)
403403
require.Equal(t, token.AccessToken, gotAPIKey.OAuthAccessToken)
404404
})
405+
406+
t.Run("RemoteIPUpdates", func(t *testing.T) {
407+
t.Parallel()
408+
var (
409+
db = databasefake.New()
410+
id, secret = randomAPIKeyParts()
411+
hashed = sha256.Sum256([]byte(secret))
412+
r = httptest.NewRequest("GET", "/", nil)
413+
rw = httptest.NewRecorder()
414+
user = createUser(r.Context(), t, db)
415+
)
416+
r.RemoteAddr = "1.1.1.1"
417+
r.AddCookie(&http.Cookie{
418+
Name: httpmw.SessionTokenKey,
419+
Value: fmt.Sprintf("%s-%s", id, secret),
420+
})
421+
422+
sentAPIKey, err := db.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{
423+
ID: id,
424+
HashedSecret: hashed[:],
425+
LastUsed: database.Now().AddDate(0, 0, -1),
426+
ExpiresAt: database.Now().AddDate(0, 0, 1),
427+
UserID: user.ID,
428+
})
429+
require.NoError(t, err)
430+
httpmw.ExtractAPIKey(db, nil)(successHandler).ServeHTTP(rw, r)
431+
res := rw.Result()
432+
defer res.Body.Close()
433+
require.Equal(t, http.StatusOK, res.StatusCode)
434+
435+
gotAPIKey, err := db.GetAPIKeyByID(r.Context(), id)
436+
require.NoError(t, err)
437+
438+
require.NotEqual(t, sentAPIKey.IPAddress, gotAPIKey.IPAddress)
439+
})
405440
}
406441

407442
func createUser(ctx context.Context, t *testing.T, db database.Store) database.User {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy