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