package permissionsimp import ( "context" "encoding/json" "fmt" "net/http" "github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" "github.com/tech/sendico/server/interface/api/srequest" "github.com/tech/sendico/server/interface/api/sresponse" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) func (a *PermissionsAPI) changePolicies(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc { var req srequest.ChangePolicies if err := json.NewDecoder(r.Body).Decode(&req); err != nil { a.logger.Warn("Failed to decode role policies change request", zap.Error(err)) return response.BadPayload(a.logger, mservice.Roles, err) } if req.Add != nil && req.Remove != nil { for _, addItem := range *req.Add { for _, removeItem := range *req.Remove { if addItem == removeItem { a.logger.Debug("Duplicate policies found, rejecting policies update request", zap.Any("add", &addItem), zap.Any("remove", &removeItem)) return response.BadRequest(a.logger, a.Name(), "invalid_policies_change_request", "duplicate policies found in 'add' and 'remove' fields") } } } } if _, err := a.tf.CreateTransaction().Execute(r.Context(), func(ctx context.Context) (any, error) { return a.changePoliciesImp(ctx, account, &req) }); err != nil { a.logger.Debug("Rolling policies changes back", zap.Error(err)) return response.Auto(a.logger, a.Name(), err) } return response.Success(a.logger) } func (a *PermissionsAPI) changePoliciesImp( ctx context.Context, account *model.Account, req *srequest.ChangePolicies, ) (any, error) { // helper that runs through each change-item, enforces the right action, // and then calls apply(item) if enforcement passes. handle := func(items *[]model.RolePolicy, action model.Action, opName string, apply func(context.Context, *model.RolePolicy) error) error { for _, it := range *items { // 1) permission check ok, err := a.enforcer.Enforce(ctx, a.policiesPermissionRef, account.ID, it.OrganizationRef, bson.NilObjectID, action) if err != nil { a.logger.Warn(fmt.Sprintf("failed to enforce permission while %s policy", opName), zap.Error(err), zap.Any(opName, &it)) return err } if !ok { a.logger.Debug(fmt.Sprintf("policy %s denied", opName)) return merrors.AccessDenied(mservice.Policies, string(action), bson.NilObjectID) } // 2) perform the add/remove if err := apply(ctx, &it); err != nil { a.logger.Warn(fmt.Sprintf("failed to %s role policy", opName), zap.Error(err), zap.Any("policy", &it)) return err } } return nil } // REMOVE if req.Remove != nil { if err := handle(req.Remove, model.ActionDelete, "remove", func(ctx context.Context, it *model.RolePolicy) error { return a.auth.Permission().RevokeFromRole(ctx, it) }); err != nil { return nil, err } } // ADD if req.Add != nil { if err := handle(req.Add, model.ActionCreate, "add", func(ctx context.Context, it *model.RolePolicy) error { return a.auth.Permission().GrantToRole(ctx, it) }); err != nil { return nil, err } } return nil, nil }