@@ -2551,6 +2551,114 @@ func (q *querier) InsertAuditLog(ctx context.Context, arg database.InsertAuditLo
2551
2551
return insert (q .log , q .auth , rbac .ResourceAuditLog , q .db .InsertAuditLog )(ctx , arg )
2552
2552
}
2553
2553
2554
+ func (q * querier ) InsertCustomRole (ctx context.Context , arg database.InsertCustomRoleParams ) (database.CustomRole , error ) {
2555
+ // Org and site role upsert share the same query. So switch the assertion based on the org uuid.
2556
+ if arg .OrganizationID .UUID != uuid .Nil {
2557
+ if err := q .authorizeContext (ctx , policy .ActionCreate , rbac .ResourceAssignOrgRole .InOrg (arg .OrganizationID .UUID )); err != nil {
2558
+ return database.CustomRole {}, err
2559
+ }
2560
+ } else {
2561
+ if err := q .authorizeContext (ctx , policy .ActionCreate , rbac .ResourceAssignRole ); err != nil {
2562
+ return database.CustomRole {}, err
2563
+ }
2564
+ }
2565
+
2566
+ if err := q .customRoleCheck (ctx , database.CustomRole {
2567
+ Name : arg .Name ,
2568
+ DisplayName : arg .DisplayName ,
2569
+ SitePermissions : arg .SitePermissions ,
2570
+ OrgPermissions : arg .OrgPermissions ,
2571
+ UserPermissions : arg .UserPermissions ,
2572
+ CreatedAt : time .Now (),
2573
+ UpdatedAt : time .Now (),
2574
+ OrganizationID : arg .OrganizationID ,
2575
+ ID : uuid .New (),
2576
+ }); err != nil {
2577
+ return database.CustomRole {}, err
2578
+ }
2579
+ return q .db .InsertCustomRole (ctx , arg )
2580
+ }
2581
+
2582
+ // customRoleCheck will validate a custom role for inserting or updating.
2583
+ // If the role is not valid, an error will be returned.
2584
+ // - Check custom roles are valid for their resource types + actions
2585
+ // - Check the actor can create the custom role
2586
+ // - Check the custom role does not grant perms the actor does not have
2587
+ // - Prevent negative perms
2588
+ // - Prevent roles with site and org permissions.
2589
+ func (q * querier ) customRoleCheck (ctx context.Context , role database.CustomRole ) error {
2590
+ act , ok := ActorFromContext (ctx )
2591
+ if ! ok {
2592
+ return NoActorError
2593
+ }
2594
+
2595
+ // Org permissions require an org role
2596
+ if role .OrganizationID .UUID == uuid .Nil && len (role .OrgPermissions ) > 0 {
2597
+ return xerrors .Errorf ("organization permissions require specifying an organization id" )
2598
+ }
2599
+
2600
+ // Org roles can only specify org permissions
2601
+ if role .OrganizationID .UUID != uuid .Nil && (len (role .SitePermissions ) > 0 || len (role .UserPermissions ) > 0 ) {
2602
+ return xerrors .Errorf ("organization roles specify site or user permissions" )
2603
+ }
2604
+
2605
+ // The rbac.Role has a 'Valid()' function on it that will do a lot
2606
+ // of checks.
2607
+ rbacRole , err := rolestore .ConvertDBRole (database.CustomRole {
2608
+ Name : role .Name ,
2609
+ DisplayName : role .DisplayName ,
2610
+ SitePermissions : role .SitePermissions ,
2611
+ OrgPermissions : role .OrgPermissions ,
2612
+ UserPermissions : role .UserPermissions ,
2613
+ OrganizationID : role .OrganizationID ,
2614
+ })
2615
+ if err != nil {
2616
+ return xerrors .Errorf ("invalid args: %w" , err )
2617
+ }
2618
+
2619
+ err = rbacRole .Valid ()
2620
+ if err != nil {
2621
+ return xerrors .Errorf ("invalid role: %w" , err )
2622
+ }
2623
+
2624
+ if len (rbacRole .Org ) > 0 && len (rbacRole .Site ) > 0 {
2625
+ // This is a choice to keep roles simple. If we allow mixing site and org scoped perms, then knowing who can
2626
+ // do what gets more complicated.
2627
+ return xerrors .Errorf ("invalid custom role, cannot assign both org and site permissions at the same time" )
2628
+ }
2629
+
2630
+ if len (rbacRole .Org ) > 1 {
2631
+ // Again to avoid more complexity in our roles
2632
+ return xerrors .Errorf ("invalid custom role, cannot assign permissions to more than 1 org at a time" )
2633
+ }
2634
+
2635
+ // Prevent escalation
2636
+ for _ , sitePerm := range rbacRole .Site {
2637
+ err := q .customRoleEscalationCheck (ctx , act , sitePerm , rbac.Object {Type : sitePerm .ResourceType })
2638
+ if err != nil {
2639
+ return xerrors .Errorf ("site permission: %w" , err )
2640
+ }
2641
+ }
2642
+
2643
+ for orgID , perms := range rbacRole .Org {
2644
+ for _ , orgPerm := range perms {
2645
+ err := q .customRoleEscalationCheck (ctx , act , orgPerm , rbac.Object {OrgID : orgID , Type : orgPerm .ResourceType })
2646
+ if err != nil {
2647
+ return xerrors .Errorf ("org=%q: %w" , orgID , err )
2648
+ }
2649
+ }
2650
+ }
2651
+
2652
+ for _ , userPerm := range rbacRole .User {
2653
+ err := q .customRoleEscalationCheck (ctx , act , userPerm , rbac.Object {Type : userPerm .ResourceType , Owner : act .ID })
2654
+ if err != nil {
2655
+ return xerrors .Errorf ("user permission: %w" , err )
2656
+ }
2657
+ }
2658
+
2659
+ return nil
2660
+ }
2661
+
2554
2662
func (q * querier ) InsertDBCryptKey (ctx context.Context , arg database.InsertDBCryptKeyParams ) error {
2555
2663
if err := q .authorizeContext (ctx , policy .ActionCreate , rbac .ResourceSystem ); err != nil {
2556
2664
return err
@@ -3002,6 +3110,10 @@ func (q *querier) UpdateAPIKeyByID(ctx context.Context, arg database.UpdateAPIKe
3002
3110
return update (q .log , q .auth , fetch , q .db .UpdateAPIKeyByID )(ctx , arg )
3003
3111
}
3004
3112
3113
+ func (q * querier ) UpdateCustomRole (ctx context.Context , arg database.UpdateCustomRoleParams ) (database.CustomRole , error ) {
3114
+ panic ("not implemented" )
3115
+ }
3116
+
3005
3117
func (q * querier ) UpdateExternalAuthLink (ctx context.Context , arg database.UpdateExternalAuthLinkParams ) (database.ExternalAuthLink , error ) {
3006
3118
fetch := func (ctx context.Context , arg database.UpdateExternalAuthLinkParams ) (database.ExternalAuthLink , error ) {
3007
3119
return q .db .GetExternalAuthLink (ctx , database.GetExternalAuthLinkParams {UserID : arg .UserID , ProviderID : arg .ProviderID })
@@ -3664,91 +3776,6 @@ func (q *querier) UpsertApplicationName(ctx context.Context, value string) error
3664
3776
return q .db .UpsertApplicationName (ctx , value )
3665
3777
}
3666
3778
3667
- // UpsertCustomRole does a series of authz checks to protect custom roles.
3668
- // - Check custom roles are valid for their resource types + actions
3669
- // - Check the actor can create the custom role
3670
- // - Check the custom role does not grant perms the actor does not have
3671
- // - Prevent negative perms
3672
- // - Prevent roles with site and org permissions.
3673
- func (q * querier ) UpsertCustomRole (ctx context.Context , arg database.UpsertCustomRoleParams ) (database.CustomRole , error ) {
3674
- act , ok := ActorFromContext (ctx )
3675
- if ! ok {
3676
- return database.CustomRole {}, NoActorError
3677
- }
3678
-
3679
- // Org and site role upsert share the same query. So switch the assertion based on the org uuid.
3680
- if arg .OrganizationID .UUID != uuid .Nil {
3681
- if err := q .authorizeContext (ctx , policy .ActionCreate , rbac .ResourceAssignOrgRole .InOrg (arg .OrganizationID .UUID )); err != nil {
3682
- return database.CustomRole {}, err
3683
- }
3684
- } else {
3685
- if err := q .authorizeContext (ctx , policy .ActionCreate , rbac .ResourceAssignRole ); err != nil {
3686
- return database.CustomRole {}, err
3687
- }
3688
- }
3689
-
3690
- if arg .OrganizationID .UUID == uuid .Nil && len (arg .OrgPermissions ) > 0 {
3691
- return database.CustomRole {}, xerrors .Errorf ("organization permissions require specifying an organization id" )
3692
- }
3693
-
3694
- // There is quite a bit of validation we should do here.
3695
- // The rbac.Role has a 'Valid()' function on it that will do a lot
3696
- // of checks.
3697
- rbacRole , err := rolestore .ConvertDBRole (database.CustomRole {
3698
- Name : arg .Name ,
3699
- DisplayName : arg .DisplayName ,
3700
- SitePermissions : arg .SitePermissions ,
3701
- OrgPermissions : arg .OrgPermissions ,
3702
- UserPermissions : arg .UserPermissions ,
3703
- OrganizationID : arg .OrganizationID ,
3704
- })
3705
- if err != nil {
3706
- return database.CustomRole {}, xerrors .Errorf ("invalid args: %w" , err )
3707
- }
3708
-
3709
- err = rbacRole .Valid ()
3710
- if err != nil {
3711
- return database.CustomRole {}, xerrors .Errorf ("invalid role: %w" , err )
3712
- }
3713
-
3714
- if len (rbacRole .Org ) > 0 && len (rbacRole .Site ) > 0 {
3715
- // This is a choice to keep roles simple. If we allow mixing site and org scoped perms, then knowing who can
3716
- // do what gets more complicated.
3717
- return database.CustomRole {}, xerrors .Errorf ("invalid custom role, cannot assign both org and site permissions at the same time" )
3718
- }
3719
-
3720
- if len (rbacRole .Org ) > 1 {
3721
- // Again to avoid more complexity in our roles
3722
- return database.CustomRole {}, xerrors .Errorf ("invalid custom role, cannot assign permissions to more than 1 org at a time" )
3723
- }
3724
-
3725
- // Prevent escalation
3726
- for _ , sitePerm := range rbacRole .Site {
3727
- err := q .customRoleEscalationCheck (ctx , act , sitePerm , rbac.Object {Type : sitePerm .ResourceType })
3728
- if err != nil {
3729
- return database.CustomRole {}, xerrors .Errorf ("site permission: %w" , err )
3730
- }
3731
- }
3732
-
3733
- for orgID , perms := range rbacRole .Org {
3734
- for _ , orgPerm := range perms {
3735
- err := q .customRoleEscalationCheck (ctx , act , orgPerm , rbac.Object {OrgID : orgID , Type : orgPerm .ResourceType })
3736
- if err != nil {
3737
- return database.CustomRole {}, xerrors .Errorf ("org=%q: %w" , orgID , err )
3738
- }
3739
- }
3740
- }
3741
-
3742
- for _ , userPerm := range rbacRole .User {
3743
- err := q .customRoleEscalationCheck (ctx , act , userPerm , rbac.Object {Type : userPerm .ResourceType , Owner : act .ID })
3744
- if err != nil {
3745
- return database.CustomRole {}, xerrors .Errorf ("user permission: %w" , err )
3746
- }
3747
- }
3748
-
3749
- return q .db .UpsertCustomRole (ctx , arg )
3750
- }
3751
-
3752
3779
func (q * querier ) UpsertDefaultProxy (ctx context.Context , arg database.UpsertDefaultProxyParams ) error {
3753
3780
if err := q .authorizeContext (ctx , policy .ActionUpdate , rbac .ResourceSystem ); err != nil {
3754
3781
return err
0 commit comments