UNPKG

bc-code-intelligence-mcp

Version:

BC Code Intelligence MCP Server - Complete Specialist Bundle with AI-driven expert consultation, seamless handoffs, and context-preserving workflows

373 lines (308 loc) 11.3 kB
# Temporary Table Safety - AL Code Examples ## Good Examples ### Secure Temporary Table with Cleanup ```al procedure ProcessSensitiveCustomerData(CustomerFilter: Text): Boolean var Customer: Record Customer; TempCustomerBuffer: Record Customer temporary; ProcessingResult: Boolean; begin // Validate user permissions before populating temporary table if not UserPermissions.CanViewCustomerData() then exit(false); Customer.SetFilter("No.", CustomerFilter); if not Customer.FindSet() then exit(false); try // Populate temporary table with filtered data repeat // Only include data user has permission to see if UserPermissions.CanViewCustomer(Customer."No.") then begin TempCustomerBuffer := Customer; TempCustomerBuffer.Insert(); end; until Customer.Next() = 0; // Process temporary data ProcessingResult := ProcessCustomerBuffer(TempCustomerBuffer); finally // Always clear sensitive temporary data TempCustomerBuffer.DeleteAll(); end; exit(ProcessingResult); end; ``` ### Permission-Aware Temporary Processing ```al procedure GenerateCustomerReport(CustomerNo: Code[20]): Text var Customer: Record Customer; CustLedgerEntry: Record "Cust. Ledger Entry"; TempReportBuffer: Record "Name/Value Buffer" temporary; ReportText: Text; begin // Early permission validation if not Customer.ReadPermission then exit('Access Denied'); if not Customer.Get(CustomerNo) then exit('Customer Not Found'); // Verify specific customer access if not UserPermissions.CanViewCustomer(CustomerNo) then exit('Access Denied'); try // Build temporary report data with permission checks TempReportBuffer.Init(); TempReportBuffer.Name := 'Customer Name'; TempReportBuffer.Value := Customer.Name; TempReportBuffer.Insert(); // Only include financial data if user has permission if CustLedgerEntry.ReadPermission and UserPermissions.CanViewFinancialData() then begin CustLedgerEntry.SetRange("Customer No.", CustomerNo); CustLedgerEntry.CalcSums("Remaining Amt. (LCY)"); TempReportBuffer.Init(); TempReportBuffer.Name := 'Outstanding Amount'; TempReportBuffer.Value := Format(CustLedgerEntry."Remaining Amt. (LCY)"); TempReportBuffer.Insert(); end; // Generate report from temporary data ReportText := BuildReportFromBuffer(TempReportBuffer); finally // Always clear temporary data containing sensitive information TempReportBuffer.DeleteAll(); end; exit(ReportText); end; ``` ### Scoped Temporary Table Processing ```al procedure CalculateItemProfitability(ItemFilter: Text): Decimal var Item: Record Item; ItemLedgerEntry: Record "Item Ledger Entry"; TempProfitBuffer: Record "Integer" temporary; TotalProfit: Decimal; LineNo: Integer; begin // Validate permissions for cost data access if not UserPermissions.CanViewCostInformation() then exit(0); Item.SetFilter("No.", ItemFilter); if not Item.FindSet() then exit(0); try LineNo := 0; repeat // Only process items user can view if UserPermissions.CanViewItem(Item."No.") then begin LineNo += 10000; // Calculate profit for this item ItemLedgerEntry.SetRange("Item No.", Item."No."); ItemLedgerEntry.CalcSums("Sales Amount (Actual)", "Cost Amount (Actual)"); // Store in temporary buffer with limited scope TempProfitBuffer.Init(); TempProfitBuffer.Number := LineNo; TempProfitBuffer.Insert(); TotalProfit += (ItemLedgerEntry."Sales Amount (Actual)" - ItemLedgerEntry."Cost Amount (Actual)"); end; until Item.Next() = 0; finally // Cleanup temporary data TempProfitBuffer.DeleteAll(); end; exit(TotalProfit); end; ``` ## Bad Examples ### Temporary Table Without Permission Validation ```al procedure ProcessAllCustomerData(): Boolean var Customer: Record Customer; TempCustomerBuffer: Record Customer temporary; begin // BAD: No permission validation before accessing sensitive data Customer.FindSet(); repeat TempCustomerBuffer := Customer; TempCustomerBuffer.Insert(); until Customer.Next() = 0; // BAD: Processing all customer data regardless of user permissions ProcessCustomerBuffer(TempCustomerBuffer); // BAD: No cleanup of sensitive temporary data exit(true); end; ``` ### No Cleanup in Error Scenarios ```al procedure RiskyTemporaryProcessing(CustomerNo: Code[20]): Boolean var Customer: Record Customer; TempSensitiveData: Record Customer temporary; begin if Customer.Get(CustomerNo) then begin TempSensitiveData := Customer; TempSensitiveData.Insert(); // BAD: If this processing fails, temporary data is not cleaned up if ProcessSensitiveData(TempSensitiveData) then begin TempSensitiveData.DeleteAll(); exit(true); end; // BAD: Error path doesn't clean up temporary data exit(false); end; end; ``` ### Cross-Session Data Leakage Risk ```al codeunit 50100 "Unsafe Temporary Processing" { // BAD: Global temporary table could leak data between sessions var GlobalTempBuffer: Record Customer temporary; procedure ProcessCustomer(CustomerNo: Code[20]): Boolean begin // BAD: Global temporary data persists across procedure calls GlobalTempBuffer."No." := CustomerNo; GlobalTempBuffer.Insert(); // BAD: No session isolation or cleanup exit(ProcessGlobalBuffer()); end; } ``` ## Best Practices ### Comprehensive Error Handling ```al procedure SecureTemporaryProcessing(RecordID: RecordID): Boolean var RecordRef: RecordRef; TempProcessingBuffer: Record "Integer" temporary; ProcessingSuccess: Boolean; ErrorOccurred: Boolean; begin // Early permission validation if not UserPermissions.CanAccessRecord(RecordID) then exit(false); if not RecordRef.Get(RecordID) then exit(false); try // Initialize temporary processing with minimal data TempProcessingBuffer.Init(); TempProcessingBuffer.Number := RecordID.RecordId; TempProcessingBuffer.Insert(); // Secure processing with error handling ProcessingSuccess := ProcessRecordSecurely(RecordRef, TempProcessingBuffer); except ErrorOccurred := true; end; // Always cleanup, even in error scenarios TempProcessingBuffer.DeleteAll(); if ErrorOccurred then exit(false); exit(ProcessingSuccess); end; ``` ### Session-Isolated Temporary Processing ```al procedure SessionSafeProcessing(UserID: Code[50]): Boolean var TempUserBuffer: Record User temporary; SessionGuid: Guid; begin // Create session-specific identifier SessionGuid := CreateGuid(); // Validate current user permissions if (UserID <> UserId) and (not UserPermissions.CanViewOtherUsers()) then exit(false); try // Session-isolated temporary data TempUserBuffer.Init(); TempUserBuffer."User Security ID" := SessionGuid; TempUserBuffer."User Name" := UserID; TempUserBuffer.Insert(); // Process with session context exit(ProcessUserInSession(TempUserBuffer, SessionGuid)); finally // Session cleanup TempUserBuffer.DeleteAll(); end; end; ``` ### Audit-Compliant Temporary Processing ```al procedure AuditSafeProcessing(DocumentNo: Code[20]): Boolean var SalesHeader: Record "Sales Header"; TempAuditBuffer: Record "Change Log Entry" temporary; AuditEntryNo: Integer; begin // Permission and audit validation if not UserPermissions.CanViewDocument(DocumentNo) then begin LogSecurityEvent('Unauthorized access attempt', DocumentNo); exit(false); end; if not SalesHeader.Get(SalesHeader."Document Type"::Order, DocumentNo) then exit(false); try // Create audit trail for temporary data access AuditEntryNo := CreateAuditEntry('Temporary Processing', DocumentNo); // Limited temporary data with audit context TempAuditBuffer.Init(); TempAuditBuffer."Entry No." := AuditEntryNo; TempAuditBuffer."Table No." := Database::"Sales Header"; TempAuditBuffer."Primary Key Field 1 Value" := DocumentNo; TempAuditBuffer.Insert(); // Audited processing exit(ProcessWithAuditTrail(SalesHeader, TempAuditBuffer)); finally // Cleanup with audit completion TempAuditBuffer.DeleteAll(); CompleteAuditEntry(AuditEntryNo); end; end; ``` ### Memory-Efficient Cleanup ```al procedure LargeDatasetProcessing(FilterText: Text): Boolean var SourceRecord: Record "Item Ledger Entry"; TempProcessingBuffer: Record "Item Ledger Entry" temporary; BatchSize: Integer; ProcessedCount: Integer; begin BatchSize := 1000; // Process in batches to manage memory SourceRecord.SetFilter("Item No.", FilterText); if not SourceRecord.FindSet() then exit(false); repeat try // Process in batches with cleanup ProcessedCount := 0; repeat TempProcessingBuffer := SourceRecord; TempProcessingBuffer.Insert(); ProcessedCount += 1; // Batch boundary cleanup if ProcessedCount >= BatchSize then begin ProcessBatch(TempProcessingBuffer); TempProcessingBuffer.DeleteAll(); ProcessedCount := 0; end; until (SourceRecord.Next() = 0) or (ProcessedCount >= BatchSize); // Process remaining records if ProcessedCount > 0 then ProcessBatch(TempProcessingBuffer); finally // Always cleanup batch data TempProcessingBuffer.DeleteAll(); end; until SourceRecord.Next() = 0; exit(true); end; ``` ## Security Guidelines 1. **Validate permissions before populating temporary tables with sensitive data** 2. **Use try-finally blocks to ensure cleanup even in error scenarios** 3. **Apply session isolation to prevent cross-user data leakage** 4. **Implement audit trails for temporary access to sensitive information** 5. **Process large datasets in batches with regular cleanup** 6. **Document temporary table security requirements and cleanup procedures**