RAP Business Objects
RAP / CDS / ODataintermediate

RAP Business Objects Part 5: Authorization, Feature Control and Messages

Key takeaway

Separate three controls that are often confused — authorization (who), feature control (when), and access control (which rows) — and learn how RAP messages reach the user correctly with FAILED, REPORTED, message classes and field highlighting.

Explanation

📌 KEY POINTS
✅ Authorization = WHO, feature control = WHEN, DCL = WHICH ROWS.

✅ Global auth for create; instance auth for update/delete/actions.

✅ Every FAILED needs a REPORTED message from a message class.

⚠️ Use %state_area so fixed errors disappear in draft.

▶ WHERE WE ARE
Parts 3–4 covered transactions and concurrency. This last part covers the controls that decide whether an operation is allowed at all, and how the user learns why something was rejected.

━━━━━━━━━━━━━━━━━━━━
▶ THREE CONTROLS, THREE QUESTIONS

  • Authorization — WHO may do it? Depends on the user's roles. "Only buyers may create requests."
  • Feature control — WHEN is it possible? Depends on the data's state. "Approve only when status is Submitted."
  • Access control (CDS DCL) — WHICH rows can the user read? "Managers see only their region." (CDS Views Part 4.)

Mixing them up is a classic design error: a state rule coded as authorization makes the support desk chase missing roles; a role check coded as feature control disappears when someone calls the API with different field values.

━━━━━━━━━━━━━━━━━━━━
▶ AUTHORIZATION

Declared with "authorization master ( global, instance )" on the root and "authorization dependent by _Parent" on children.

  • Global authorization (get_global_authorizations) — operations that do not need an instance: create, static actions. "May this user create purchase requests at all?"
  • Instance authorization (get_instance_authorizations) — operations on an existing instance: update, delete, instance actions. "May this user change request 42?" — for example depending on its company code.

Implement with AUTHORITY-CHECK against your authorization object and return auth-allowed or auth-unauthorized. For operations that need no check, declare "( authorization : none )" explicitly — never leave it to chance.

━━━━━━━━━━━━━━━━━━━━
▶ FEATURE CONTROL

  • Static — declared in the BDEF, always the same: "field ( readonly ) Status;", "field ( mandatory ) Description;", "field ( readonly : update ) RequestID;" (settable on create only).
  • Instance (dynamic) — declared with "( features : instance )" on operations, actions or fields; implemented in get_instance_features by reading the instance state and returning fc-o-enabled/disabled (operations, actions) or fc-f-read_only/mandatory/unrestricted (fields).

Fiori uses feature control to grey out buttons and make fields read-only; API calls that ignore it are rejected by RAP.

━━━━━━━━━━━━━━━━━━━━
▶ MESSAGES

Every rejection must tell the user why. The rules:

  • FAILED — list the instance (with %tky, or %cid for new ones) that failed; optionally %fail-cause (not_found, unauthorized, locked, conflict).
  • REPORTED — add a message object (%msg) for the same instance. Without it the user sees a generic error.
  • Use a message class: new_message( id = 'ZPR' number = '001' severity = ... v1 = ... ) so texts are translatable and consistent. new_message_with_text is fine for prototypes.
  • %element-<field> = if_abap_behv=>mk-on — Fiori highlights the field.
  • %state_area — groups validation messages so they are removed when the problem is fixed (important in draft).
  • Severity: error blocks, warning and information only inform, success confirms. Only errors (with FAILED) stop processing.
  • Messages not tied to an instance (e.g. from a static action) go into REPORTED with %other.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. For every rule in your requirements, write "who / when / which rows" next to it.
  2. Put "who" rules into authorization, "when" rules into feature control, "which rows" into DCL.
  3. Create a message class for the BO and use it everywhere.
  4. Test with a restricted user and with each status: buttons, fields and messages must match the rules.

━━━━━━━━━━━━━━━━━━━━
▶ WHAT YOU HAVE AFTER THE SERIES

The complete conceptual model of a RAP BO: tree, implementation type, transaction, concurrency, controls and messages. Next topic: Behavior Definitions — the exact syntax that expresses all of this.

Code example

ABAP Code
// ===== BDEF: where each control is declared =====define behavior for ZR_PurchReq alias PurchaseRequestpersistent table zpr_headlock masterauthorization master ( global, instance )           // WHO: global + instanceetag master LocalLastChangedAt{  create;  update ( features : instance );                   // WHEN: state-dependent  delete ( features : instance );  action ( features : instance ) approve result [1] $self;  static action ( authorization : none ) refreshCatalog;               // explicitly unchecked   field ( readonly ) Status, RequestID;             // static field control  field ( mandatory ) Description, CurrencyCode;  field ( features : instance ) Supplier;           // dynamic field control} " ===== Instance authorization (WHO) =====METHOD get_instance_authorizations.  AUTHORITY-CHECK OBJECT 'ZPR_REQ' ID 'ACTVT' FIELD '02'.  DATA(may_change) = xsdbool( sy-subrc = 0 ).  result = VALUE #( FOR key IN keys    ( %tky    = key-%tky      %update = COND #( WHEN may_change = abap_true THEN if_abap_behv=>auth-allowed                                                    ELSE if_abap_behv=>auth-unauthorized ) ) ).ENDMETHOD. " ===== Instance feature control (WHEN) =====METHOD get_instance_features.  READ ENTITIES OF zr_purchreq IN LOCAL MODE    ENTITY PurchaseRequest FIELDS ( Status ) WITH CORRESPONDING #( keys )    RESULT DATA(requests).  result = VALUE #( FOR r IN requests    ( %tky            = r-%tky      %update         = COND #( WHEN r-Status = 'A' THEN if_abap_behv=>fc-o-disabled ELSE if_abap_behv=>fc-o-enabled )      %action-approve = COND #( WHEN r-Status = 'S' THEN if_abap_behv=>fc-o-enabled  ELSE if_abap_behv=>fc-o-disabled )      %field-Supplier = COND #( WHEN r-Status = 'N' THEN if_abap_behv=>fc-f-unrestricted ELSE if_abap_behv=>fc-f-read_only ) ) ).ENDMETHOD. " ===== A good message (in a validation) =====APPEND VALUE #( %tky = request-%tky ) TO failed-purchaserequest.APPEND VALUE #( %tky        = request-%tky                %state_area = 'VALIDATE_SUPPLIER'                %msg        = new_message( id       = 'ZPR'                                           number   = '004'          " Supplier &1 is blocked                                           severity = if_abap_behv_message=>severity-error                                           v1       = request-Supplier )                %element-Supplier = if_abap_behv=>mk-on )            " highlight field  TO reported-purchaserequest.

Real project scenario

Support received many tickets saying "I have no authorization to change the request", although the users had the correct roles. The rule "approved requests cannot be changed" had been implemented in get_instance_authorizations, so RAP reported an authorization failure. Support spent hours comparing roles.
The team moved the rule to instance feature control. The Edit button now disappears for approved requests, and API attempts return a clear message instead of an authorization error. Real authorization failures became rare and meaningful again, and the tickets stopped.

Common mistakes

  • Implementing state rules as authorization or role rules as feature control.
  • Forgetting that create is checked by global authorization.
  • FAILED without a REPORTED message, or a message without %element.
  • Hard-coded texts everywhere instead of a message class.
  • Missing %state_area, so old draft messages stay visible after the fix.
  • Relying only on the UI greying out buttons instead of feature control that RAP enforces.
  • Using DCL to try to prevent changes (it only restricts reads).

Best practices

  • Classify every rule: who (authorization), when (feature control), which rows (DCL).
  • Declare "authorization : none" explicitly where no check is intended.
  • Prefer static field control in the BDEF; use instance feature control only when state matters.
  • One message class per BO; always set %element and %state_area in validations.
  • Test with restricted users and every status value.

Interview angle

Questions: "Authorization vs feature control?", "Global vs instance authorization?", "How do you report an error to the user in RAP?"
Strong answer: authorization depends on the user, feature control on the instance state, DCL restricts readable rows. Global authorization covers create and static actions, instance authorization covers update/delete/instance actions. Errors are returned as FAILED (with %fail-cause) plus REPORTED with a message from a message class, %element for field highlighting and %state_area for draft. Giving the "support chased missing roles" example shows real experience.

Practice this lesson

Quick check

1. A validation appends an instance to FAILED but adds nothing to REPORTED. What does the user experience?

2. Which rule belongs in instance feature control rather than authorization?

3. An action calls a legacy function that ends with COMMIT WORK. Which problem does this cause?

Interview questions

1. Explain the difference between authorization, feature control and CDS access control in RAP, with an example of each.

Answer it in your own words first, then compare.

2. How do you design RAP messages so that users understand what went wrong and messages disappear once the problem is fixed?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics