@@ -436,6 +436,35 @@ var (
436
436
rbac .ResourceProvisionerJobs .Type : {policy .ActionRead ,policy .ActionUpdate ,policy .ActionCreate },
437
437
rbac .ResourceOauth2App .Type : {policy .ActionCreate ,policy .ActionRead ,policy .ActionUpdate ,policy .ActionDelete },
438
438
rbac .ResourceOauth2AppSecret .Type : {policy .ActionCreate ,policy .ActionRead ,policy .ActionUpdate ,policy .ActionDelete },
439
+ rbac .ResourceOauth2AppCodeToken .Type : {policy .ActionCreate ,policy .ActionRead ,policy .ActionUpdate ,policy .ActionDelete },
440
+ }),
441
+ Org :map [string ][]rbac.Permission {},
442
+ User : []rbac.Permission {},
443
+ },
444
+ }),
445
+ Scope :rbac .ScopeAll ,
446
+ }.WithCachedASTValue ()
447
+
448
+ subjectSystemOAuth2 = rbac.Subject {
449
+ Type :rbac .SubjectTypeSystemRestricted ,
450
+ FriendlyName :"System OAuth2" ,
451
+ ID :uuid .Nil .String (),
452
+ Roles :rbac .Roles ([]rbac.Role {
453
+ {
454
+ Identifier : rbac.RoleIdentifier {Name :"system-oauth2" },
455
+ DisplayName :"System OAuth2" ,
456
+ Site :rbac .Permissions (map [string ][]policy.Action {
457
+ // OAuth2 resources - full CRUD permissions
458
+ rbac .ResourceOauth2App .Type :rbac .ResourceOauth2App .AvailableActions (),
459
+ rbac .ResourceOauth2AppSecret .Type :rbac .ResourceOauth2AppSecret .AvailableActions (),
460
+ rbac .ResourceOauth2AppCodeToken .Type :rbac .ResourceOauth2AppCodeToken .AvailableActions (),
461
+
462
+ // API key permissions needed for OAuth2 token revocation
463
+ rbac .ResourceApiKey .Type : {policy .ActionRead ,policy .ActionDelete },
464
+
465
+ // Minimal read permissions that might be needed for OAuth2 operations
466
+ rbac .ResourceUser .Type : {policy .ActionRead },
467
+ rbac .ResourceOrganization .Type : {policy .ActionRead },
439
468
}),
440
469
User : []rbac.Permission {},
441
470
ByOrgID :map [string ]rbac.OrgPermissions {},
@@ -641,6 +670,12 @@ func AsSystemRestricted(ctx context.Context) context.Context {
641
670
return As (ctx ,subjectSystemRestricted )
642
671
}
643
672
673
+ // AsSystemOAuth2 returns a context with an actor that has permissions
674
+ // required for OAuth2 provider operations (token revocation, device codes, registration).
675
+ func AsSystemOAuth2 (ctx context.Context ) context.Context {
676
+ return As (ctx ,subjectSystemOAuth2 )
677
+ }
678
+
644
679
// AsSystemReadProvisionerDaemons returns a context with an actor that has permissions
645
680
// to read provisioner daemons.
646
681
func AsSystemReadProvisionerDaemons (ctx context.Context ) context.Context {
@@ -1434,6 +1469,14 @@ func (q *querier) CleanTailnetTunnels(ctx context.Context) error {
1434
1469
return q .db .CleanTailnetTunnels (ctx )
1435
1470
}
1436
1471
1472
+ func (q * querier )ConsumeOAuth2ProviderAppCodeByPrefix (ctx context.Context ,secretPrefix []byte ) (database.OAuth2ProviderAppCode ,error ) {
1473
+ return updateWithReturn (q .log ,q .auth ,q .db .GetOAuth2ProviderAppCodeByPrefix ,q .db .ConsumeOAuth2ProviderAppCodeByPrefix )(ctx ,secretPrefix )
1474
+ }
1475
+
1476
+ func (q * querier )ConsumeOAuth2ProviderDeviceCodeByPrefix (ctx context.Context ,deviceCodePrefix string ) (database.OAuth2ProviderDeviceCode ,error ) {
1477
+ return updateWithReturn (q .log ,q .auth ,q .db .GetOAuth2ProviderDeviceCodeByPrefix ,q .db .ConsumeOAuth2ProviderDeviceCodeByPrefix )(ctx ,deviceCodePrefix )
1478
+ }
1479
+
1437
1480
func (q * querier )CountAuditLogs (ctx context.Context ,arg database.CountAuditLogsParams ) (int64 ,error ) {
1438
1481
// Shortcut if the user is an owner. The SQL filter is noticeable,
1439
1482
// and this is an easy win for owners. Which is the common case.
@@ -1577,7 +1620,7 @@ func (q *querier) DeleteExpiredOAuth2ProviderDeviceCodes(ctx context.Context) er
1577
1620
func (q * querier )DeleteExternalAuthLink (ctx context.Context ,arg database.DeleteExternalAuthLinkParams )error {
1578
1621
return fetchAndExec (q .log ,q .auth ,policy .ActionUpdatePersonal ,func (ctx context.Context ,arg database.DeleteExternalAuthLinkParams ) (database.ExternalAuthLink ,error ) {
1579
1622
//nolint:gosimple
1580
- return q .db .GetExternalAuthLink (ctx , database.GetExternalAuthLinkParams { UserID : arg . UserID , ProviderID : arg . ProviderID } )
1623
+ return q .db .GetExternalAuthLink (ctx ,database .GetExternalAuthLinkParams ( arg ) )
1581
1624
},q .db .DeleteExternalAuthLink )(ctx ,arg )
1582
1625
}
1583
1626
@@ -1656,27 +1699,30 @@ func (q *querier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Contex
1656
1699
return q .db .DeleteOAuth2ProviderAppTokensByAppAndUserID (ctx ,arg )
1657
1700
}
1658
1701
1659
- func (q * querier )DeleteOldAuditLogConnectionEvents (ctx context.Context ,threshold database.DeleteOldAuditLogConnectionEventsParams )error {
1660
- // `ResourceSystem` is deprecated, but it doesn't make sense to add
1661
- // `policy.ActionDelete` to `ResourceAuditLog`, since this is the one and
1662
- // only time we'll be deleting from the audit log.
1663
- if err := q .authorizeContext (ctx ,policy .ActionDelete ,rbac .ResourceSystem );err != nil {
1664
- return err
1665
- }
1666
- return q .db .DeleteOldAuditLogConnectionEvents (ctx ,threshold )
1667
- }
1668
-
1669
1702
func (q * querier )DeleteOAuth2ProviderDeviceCodeByID (ctx context.Context ,id uuid.UUID )error {
1670
1703
// Fetch the device code first to check authorization
1671
1704
deviceCode ,err := q .db .GetOAuth2ProviderDeviceCodeByID (ctx ,id )
1672
1705
if err != nil {
1673
- return err
1706
+ return xerrors . Errorf ( "get oauth2 provider device code: %w" , err )
1674
1707
}
1675
1708
if err := q .authorizeContext (ctx ,policy .ActionDelete ,deviceCode );err != nil {
1676
- return err
1709
+ return xerrors .Errorf ("authorize oauth2 provider device code deletion: %w" ,err )
1710
+ }
1711
+
1712
+ if err := q .db .DeleteOAuth2ProviderDeviceCodeByID (ctx ,id );err != nil {
1713
+ return xerrors .Errorf ("delete oauth2 provider device code: %w" ,err )
1677
1714
}
1715
+ return nil
1716
+ }
1678
1717
1679
- return q .db .DeleteOAuth2ProviderDeviceCodeByID (ctx ,id )
1718
+ func (q * querier )DeleteOldAuditLogConnectionEvents (ctx context.Context ,threshold database.DeleteOldAuditLogConnectionEventsParams )error {
1719
+ // `ResourceSystem` is deprecated, but it doesn't make sense to add
1720
+ // `policy.ActionDelete` to `ResourceAuditLog`, since this is the one and
1721
+ // only time we'll be deleting from the audit log.
1722
+ if err := q .authorizeContext (ctx ,policy .ActionDelete ,rbac .ResourceSystem );err != nil {
1723
+ return err
1724
+ }
1725
+ return q .db .DeleteOldAuditLogConnectionEvents (ctx ,threshold )
1680
1726
}
1681
1727
1682
1728
func (q * querier )DeleteOldNotificationMessages (ctx context.Context )error {
@@ -1708,7 +1754,7 @@ func (q *querier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
1708
1754
}
1709
1755
1710
1756
func (q * querier )DeleteOrganizationMember (ctx context.Context ,arg database.DeleteOrganizationMemberParams )error {
1711
- return deleteQ [database. OrganizationMember ] (q .log ,q .auth ,func (ctx context.Context ,arg database.DeleteOrganizationMemberParams ) (database.OrganizationMember ,error ) {
1757
+ return deleteQ (q .log ,q .auth ,func (ctx context.Context ,arg database.DeleteOrganizationMemberParams ) (database.OrganizationMember ,error ) {
1712
1758
member ,err := database .ExpectOne (q .OrganizationMembers (ctx , database.OrganizationMembersParams {
1713
1759
OrganizationID :arg .OrganizationID ,
1714
1760
UserID :arg .UserID ,
@@ -2371,7 +2417,7 @@ func (q *querier) GetLicenseByID(ctx context.Context, id int32) (database.Licens
2371
2417
}
2372
2418
2373
2419
func (q * querier )GetLicenses (ctx context.Context ) ([]database.License ,error ) {
2374
- fetch := func (ctx context.Context ,_ interface {} ) ([]database.License ,error ) {
2420
+ fetch := func (ctx context.Context ,_ any ) ([]database.License ,error ) {
2375
2421
return q .db .GetLicenses (ctx )
2376
2422
}
2377
2423
return fetchWithPostFilter (q .auth ,policy .ActionRead ,fetch )(ctx ,nil )
@@ -2527,8 +2573,8 @@ func (q *querier) GetOAuth2ProviderDeviceCodeByUserCode(ctx context.Context, use
2527
2573
}
2528
2574
2529
2575
func (q * querier )GetOAuth2ProviderDeviceCodesByClientID (ctx context.Context ,clientID uuid.UUID ) ([]database.OAuth2ProviderDeviceCode ,error ) {
2530
- // This requires access to readthe OAuth2 app
2531
- if err := q .authorizeContext (ctx ,policy .ActionRead ,rbac .ResourceOauth2App );err != nil {
2576
+ // This requires access to read OAuth2 app code tokens
2577
+ if err := q .authorizeContext (ctx ,policy .ActionRead ,rbac .ResourceOauth2AppCodeToken );err != nil {
2532
2578
return []database.OAuth2ProviderDeviceCode {},err
2533
2579
}
2534
2580
return q .db .GetOAuth2ProviderDeviceCodesByClientID (ctx ,clientID )
@@ -2585,7 +2631,7 @@ func (q *querier) GetOrganizationResourceCountByID(ctx context.Context, organiza
2585
2631
}
2586
2632
2587
2633
func (q * querier )GetOrganizations (ctx context.Context ,args database.GetOrganizationsParams ) ([]database.Organization ,error ) {
2588
- fetch := func (ctx context.Context ,_ interface {} ) ([]database.Organization ,error ) {
2634
+ fetch := func (ctx context.Context ,_ any ) ([]database.Organization ,error ) {
2589
2635
return q .db .GetOrganizations (ctx ,args )
2590
2636
}
2591
2637
return fetchWithPostFilter (q .auth ,policy .ActionRead ,fetch )(ctx ,nil )
@@ -2713,7 +2759,7 @@ func (q *querier) GetPreviousTemplateVersion(ctx context.Context, arg database.G
2713
2759
}
2714
2760
2715
2761
func (q * querier )GetProvisionerDaemons (ctx context.Context ) ([]database.ProvisionerDaemon ,error ) {
2716
- fetch := func (ctx context.Context ,_ interface {} ) ([]database.ProvisionerDaemon ,error ) {
2762
+ fetch := func (ctx context.Context ,_ any ) ([]database.ProvisionerDaemon ,error ) {
2717
2763
return q .db .GetProvisionerDaemons (ctx )
2718
2764
}
2719
2765
return fetchWithPostFilter (q .auth ,policy .ActionRead ,fetch )(ctx ,nil )
@@ -3741,7 +3787,7 @@ func (q *querier) GetWorkspaceModulesCreatedAfter(ctx context.Context, createdAt
3741
3787
}
3742
3788
3743
3789
func (q * querier )GetWorkspaceProxies (ctx context.Context ) ([]database.WorkspaceProxy ,error ) {
3744
- return fetchWithPostFilter (q .auth ,policy .ActionRead ,func (ctx context.Context ,_ interface {} ) ([]database.WorkspaceProxy ,error ) {
3790
+ return fetchWithPostFilter (q .auth ,policy .ActionRead ,func (ctx context.Context ,_ any ) ([]database.WorkspaceProxy ,error ) {
3745
3791
return q .db .GetWorkspaceProxies (ctx )
3746
3792
})(ctx ,nil )
3747
3793
}
@@ -4071,8 +4117,8 @@ func (q *querier) InsertOAuth2ProviderAppToken(ctx context.Context, arg database
4071
4117
}
4072
4118
4073
4119
func (q * querier )InsertOAuth2ProviderDeviceCode (ctx context.Context ,arg database.InsertOAuth2ProviderDeviceCodeParams ) (database.OAuth2ProviderDeviceCode ,error ) {
4074
- // Creating device codes requires OAuth2 app access
4075
- if err := q .authorizeContext (ctx ,policy .ActionCreate ,rbac .ResourceOauth2App );err != nil {
4120
+ // Creating device codes requires OAuth2 appcode token creation access
4121
+ if err := q .authorizeContext (ctx ,policy .ActionCreate ,rbac .ResourceOauth2AppCodeToken );err != nil {
4076
4122
return database.OAuth2ProviderDeviceCode {},err
4077
4123
}
4078
4124
return q .db .InsertOAuth2ProviderDeviceCode (ctx ,arg )
@@ -4386,10 +4432,11 @@ func (q *querier) InsertWorkspaceBuild(ctx context.Context, arg database.InsertW
4386
4432
return xerrors .Errorf ("get workspace by id: %w" ,err )
4387
4433
}
4388
4434
4389
- var action policy.Action = policy .ActionWorkspaceStart
4390
- if arg .Transition == database .WorkspaceTransitionDelete {
4435
+ action := policy .ActionWorkspaceStart
4436
+ switch arg .Transition {
4437
+ case database .WorkspaceTransitionDelete :
4391
4438
action = policy .ActionDelete
4392
- } else if arg . Transition == database .WorkspaceTransitionStop {
4439
+ case database .WorkspaceTransitionStop :
4393
4440
action = policy .ActionWorkspaceStop
4394
4441
}
4395
4442
@@ -4813,13 +4860,10 @@ func (q *querier) UpdateOAuth2ProviderAppSecretByID(ctx context.Context, arg dat
4813
4860
}
4814
4861
4815
4862
func (q * querier )UpdateOAuth2ProviderDeviceCodeAuthorization (ctx context.Context ,arg database.UpdateOAuth2ProviderDeviceCodeAuthorizationParams ) (database.OAuth2ProviderDeviceCode ,error ) {
4816
- // Verify the user is authenticated for device code authorization
4817
- _ ,ok := ActorFromContext (ctx )
4818
- if ! ok {
4819
- return database.OAuth2ProviderDeviceCode {},ErrNoActor
4863
+ fetch := func (ctx context.Context ,arg database.UpdateOAuth2ProviderDeviceCodeAuthorizationParams ) (database.OAuth2ProviderDeviceCode ,error ) {
4864
+ return q .db .GetOAuth2ProviderDeviceCodeByID (ctx ,arg .ID )
4820
4865
}
4821
-
4822
- return q .db .UpdateOAuth2ProviderDeviceCodeAuthorization (ctx ,arg )
4866
+ return updateWithReturn (q .log ,q .auth ,fetch ,q .db .UpdateOAuth2ProviderDeviceCodeAuthorization )(ctx ,arg )
4823
4867
}
4824
4868
4825
4869
func (q * querier )UpdateOrganization (ctx context.Context ,arg database.UpdateOrganizationParams ) (database.Organization ,error ) {