@@ -2,13 +2,14 @@ package component
22
33import (
44 "context"
5- "errors"
65 "fmt"
76
87 "opencsg.com/csghub-server/builder/accounting"
8+ "opencsg.com/csghub-server/builder/git/membership"
99 "opencsg.com/csghub-server/builder/rpc"
1010 "opencsg.com/csghub-server/builder/store/database"
1111 "opencsg.com/csghub-server/common/config"
12+ "opencsg.com/csghub-server/common/errorx"
1213 "opencsg.com/csghub-server/common/types"
1314)
1415
@@ -69,12 +70,43 @@ func NewAccountingComponent(config *config.Config) (AccountingComponent, error)
6970}
7071
7172func (ac * accountingComponentImpl ) ListMeteringsByUserIDAndTime (ctx context.Context , req types.ActStatementsReq ) (interface {}, error ) {
72- user , err := ac .userStore .FindByUsername (ctx , req .CurrentUser )
73+ if err := ac .allowQueryData (ctx , req .CurrentUser , req .UserUUID ); err != nil {
74+ return nil , errorx .Forbidden (err , map [string ]any {
75+ "user" : req .CurrentUser ,
76+ })
77+ }
78+ return ac .accountingClient .ListMeteringsByUserIDAndTime (req )
79+ }
80+
81+ // CanQueryUserData checks if the current user has permission to query the target user's data.
82+ // Permission is granted if:
83+ // 1. Current user is the same as the target user (querying own data)
84+ // 2. Current user is an admin
85+ // 3. Current user is a member of the organization that owns the target user's namespace
86+ func (ac * accountingComponentImpl ) allowQueryData (ctx context.Context , currentUser , targetUUID string ) error {
87+ user , err := ac .userSvcClient .GetUserByName (ctx , currentUser )
7388 if err != nil {
74- return nil , fmt .Errorf ("user does not exist, %w" , err )
89+ return fmt .Errorf ("current user not found: %w" , err )
7590 }
76- if user .UUID != req .UserUUID {
77- return nil , errors .New ("invalid user" )
91+
92+ if user .IsAdmin () || user .UUID == targetUUID {
93+ return nil
7894 }
79- return ac .accountingClient .ListMeteringsByUserIDAndTime (req )
95+
96+ ns , err := ac .userSvcClient .GetNameSpaceInfoByUUID (ctx , targetUUID )
97+ if err != nil {
98+ return fmt .Errorf ("target namespace not found: %w" , err )
99+ }
100+
101+ if ns .NSType != string (database .OrgNamespace ) {
102+ return fmt .Errorf ("do not have permission to query the target org's data: %w" , err )
103+ }
104+
105+ // Check if current user is member of org that owns target user's namespace
106+ role , err := ac .userSvcClient .GetMemberRoleByUUID (ctx , ns .UUID , currentUser )
107+ if err != nil || role == membership .RoleUnknown {
108+ return fmt .Errorf ("do not have permission to query the target org's data: %w" , err )
109+ }
110+
111+ return nil
80112}
0 commit comments