@@ -2,27 +2,24 @@ package coderd
2
2
3
3
import (
4
4
"context"
5
- "crypto/sha256"
6
5
"fmt"
7
- "net"
8
6
"net/http"
9
7
"strconv"
10
8
"time"
11
9
12
10
"github.com/go-chi/chi/v5"
13
11
"github.com/google/uuid"
14
12
"github.com/moby/moby/pkg/namesgenerator"
15
- "github.com/tabbed/pqtype"
16
13
"golang.org/x/xerrors"
17
14
15
+ "github.com/coder/coder/coderd/apikey"
18
16
"github.com/coder/coder/coderd/audit"
19
17
"github.com/coder/coder/coderd/database"
20
18
"github.com/coder/coder/coderd/httpapi"
21
19
"github.com/coder/coder/coderd/httpmw"
22
20
"github.com/coder/coder/coderd/rbac"
23
21
"github.com/coder/coder/coderd/telemetry"
24
22
"github.com/coder/coder/codersdk"
25
- "github.com/coder/coder/cryptorand"
26
23
)
27
24
28
25
// Creates a new token API key that effectively doesn't expire.
@@ -83,13 +80,14 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) {
83
80
return
84
81
}
85
82
86
- cookie , key , err := api .createAPIKey (ctx , createAPIKeyParams {
87
- UserID : user .ID ,
88
- LoginType : database .LoginTypeToken ,
89
- ExpiresAt : database .Now ().Add (lifeTime ),
90
- Scope : scope ,
91
- LifetimeSeconds : int64 (lifeTime .Seconds ()),
92
- TokenName : tokenName ,
83
+ cookie , key , err := api .createAPIKey (ctx , apikey.CreateParams {
84
+ UserID : user .ID ,
85
+ LoginType : database .LoginTypeToken ,
86
+ DeploymentValues : api .DeploymentValues ,
87
+ ExpiresAt : database .Now ().Add (lifeTime ),
88
+ Scope : scope ,
89
+ LifetimeSeconds : int64 (lifeTime .Seconds ()),
90
+ TokenName : tokenName ,
93
91
})
94
92
if err != nil {
95
93
if database .IsUniqueViolation (err , database .UniqueIndexApiKeyName ) {
@@ -127,10 +125,11 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
127
125
user := httpmw .UserParam (r )
128
126
129
127
lifeTime := time .Hour * 24 * 7
130
- cookie , _ , err := api .createAPIKey (ctx , createAPIKeyParams {
131
- UserID : user .ID ,
132
- LoginType : database .LoginTypePassword ,
133
- RemoteAddr : r .RemoteAddr ,
128
+ cookie , _ , err := api .createAPIKey (ctx , apikey.CreateParams {
129
+ UserID : user .ID ,
130
+ DeploymentValues : api .DeploymentValues ,
131
+ LoginType : database .LoginTypePassword ,
132
+ RemoteAddr : r .RemoteAddr ,
134
133
// All api generated keys will last 1 week. Browser login tokens have
135
134
// a shorter life.
136
135
ExpiresAt : database .Now ().Add (lifeTime ),
@@ -359,33 +358,6 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) {
359
358
)
360
359
}
361
360
362
- // Generates a new ID and secret for an API key.
363
- func GenerateAPIKeyIDSecret () (id string , secret string , err error ) {
364
- // Length of an API Key ID.
365
- id , err = cryptorand .String (10 )
366
- if err != nil {
367
- return "" , "" , err
368
- }
369
- // Length of an API Key secret.
370
- secret , err = cryptorand .String (22 )
371
- if err != nil {
372
- return "" , "" , err
373
- }
374
- return id , secret , nil
375
- }
376
-
377
- type createAPIKeyParams struct {
378
- UserID uuid.UUID
379
- RemoteAddr string
380
- LoginType database.LoginType
381
-
382
- // Optional.
383
- ExpiresAt time.Time
384
- LifetimeSeconds int64
385
- Scope database.APIKeyScope
386
- TokenName string
387
- }
388
-
389
361
func (api * API ) validateAPIKeyLifetime (lifetime time.Duration ) error {
390
362
if lifetime <= 0 {
391
363
return xerrors .New ("lifetime must be positive number greater than 0" )
@@ -401,79 +373,27 @@ func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error {
401
373
return nil
402
374
}
403
375
404
- func (api * API ) createAPIKey (ctx context.Context , params createAPIKeyParams ) (* http.Cookie , * database.APIKey , error ) {
405
- keyID , keySecret , err := GenerateAPIKeyIDSecret ( )
376
+ func (api * API ) createAPIKey (ctx context.Context , params apikey. CreateParams ) (* http.Cookie , * database.APIKey , error ) {
377
+ key , sessionToken , err := apikey . Generate ( params )
406
378
if err != nil {
407
379
return nil , nil , xerrors .Errorf ("generate API key: %w" , err )
408
380
}
409
- hashed := sha256 .Sum256 ([]byte (keySecret ))
410
-
411
- // Default expires at to now+lifetime, or use the configured value if not
412
- // set.
413
- if params .ExpiresAt .IsZero () {
414
- if params .LifetimeSeconds != 0 {
415
- params .ExpiresAt = database .Now ().Add (time .Duration (params .LifetimeSeconds ) * time .Second )
416
- } else {
417
- params .ExpiresAt = database .Now ().Add (api .DeploymentValues .SessionDuration .Value ())
418
- params .LifetimeSeconds = int64 (api .DeploymentValues .SessionDuration .Value ().Seconds ())
419
- }
420
- }
421
- if params .LifetimeSeconds == 0 {
422
- params .LifetimeSeconds = int64 (time .Until (params .ExpiresAt ).Seconds ())
423
- }
424
-
425
- ip := net .ParseIP (params .RemoteAddr )
426
- if ip == nil {
427
- ip = net .IPv4 (0 , 0 , 0 , 0 )
428
- }
429
- bitlen := len (ip ) * 8
430
381
431
- scope := database .APIKeyScopeAll
432
- if params .Scope != "" {
433
- scope = params .Scope
434
- }
435
- switch scope {
436
- case database .APIKeyScopeAll , database .APIKeyScopeApplicationConnect :
437
- default :
438
- return nil , nil , xerrors .Errorf ("invalid API key scope: %q" , scope )
439
- }
440
-
441
- key , err := api .Database .InsertAPIKey (ctx , database.InsertAPIKeyParams {
442
- ID : keyID ,
443
- UserID : params .UserID ,
444
- LifetimeSeconds : params .LifetimeSeconds ,
445
- IPAddress : pqtype.Inet {
446
- IPNet : net.IPNet {
447
- IP : ip ,
448
- Mask : net .CIDRMask (bitlen , bitlen ),
449
- },
450
- Valid : true ,
451
- },
452
- // Make sure in UTC time for common time zone
453
- ExpiresAt : params .ExpiresAt .UTC (),
454
- CreatedAt : database .Now (),
455
- UpdatedAt : database .Now (),
456
- HashedSecret : hashed [:],
457
- LoginType : params .LoginType ,
458
- Scope : scope ,
459
- TokenName : params .TokenName ,
460
- })
382
+ newkey , err := api .Database .InsertAPIKey (ctx , key )
461
383
if err != nil {
462
384
return nil , nil , xerrors .Errorf ("insert API key: %w" , err )
463
385
}
464
386
465
387
api .Telemetry .Report (& telemetry.Snapshot {
466
- APIKeys : []telemetry.APIKey {telemetry .ConvertAPIKey (key )},
388
+ APIKeys : []telemetry.APIKey {telemetry .ConvertAPIKey (newkey )},
467
389
})
468
390
469
- // This format is consumed by the APIKey middleware.
470
- sessionToken := fmt .Sprintf ("%s-%s" , keyID , keySecret )
471
391
return & http.Cookie {
472
392
Name : codersdk .SessionTokenCookie ,
473
393
Value : sessionToken ,
474
394
Path : "/" ,
475
395
HttpOnly : true ,
476
396
SameSite : http .SameSiteLaxMode ,
477
397
Secure : api .SecureAuthCookie ,
478
- }, & key , nil
398
+ }, & newkey , nil
479
399
}
0 commit comments