Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This repository brings together several sample applications that demonstrate how
| **Javalin Shopping Sample** | A lightweight application using **Javalin** (minimal web framework) to showcase the AMS v4 Core API | `ams-javalin-shopping` |
| **Spring Boot Shopping Sample** | A Spring Boot application to showcase AMS **Spring Security** integration | `ams-spring-boot-shopping` |
| **CAP Spring Boot Bookshop Sample** | Demonstrates the integration of AMS into a **SAP CAP Java** Spring Boot application | `ams-cap-bookshop` |
| **CAP Value Help Sample** | Demonstrates how to configure **AMS value help** in a CAP Java application | `ams-value-help-sample` |

---

Expand Down Expand Up @@ -52,6 +53,15 @@ This repository brings together several sample applications that demonstrate how
- Contains instructions for both local development and cloud deployment
- **More info:** [CAP Bookshop Sample](ams-cap-bookshop/README.md)

### 4. CAP Value Help Sample (`ams-value-help-sample`)

- **Framework:** SAP Cloud Application Programming Model (CAP) for Java
- **Highlights:**
- Demonstrates how to enable **AMS value help** so administrators can select valid attribute values from a dropdown when creating policies in the AMS admin console
- Uses the `@valueHelp` CDS annotation to expose an OData endpoint that the AMS server calls back
- Shows how to secure the value help endpoint using an `INTERNAL POLICY` and a dedicated `AmsValueHelpService`
- **More info:** [CAP Value Help Sample](ams-value-help-sample/README.md)

---

## Legacy Samples (v3)
Expand Down
38 changes: 38 additions & 0 deletions ams-value-help-sample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
**/gen/
**/edmx/
*.db
*.sqlite
*.sqlite-wal
*.sqlite-shm
schema*.sql
default-env.json
.env

**/bin/
**/target/
.flattened-pom.xml
.classpath
.project
.settings

**/node/
**/node_modules/

**/.mta/
*.mtar
*.tar

*.log*
gc_history*
hs_err*
*.tgz
*.iml

.vscode
.idea
.reloadtrigger

**/.DS_Store

# added by cds
.cdsrc-private.json
97 changes: 97 additions & 0 deletions ams-value-help-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# AMS Value Help Sample for CAP Java

This sample demonstrates the usage of value help for AMS in a CAP Java environment. The sample uses the CAP bookshop and activates value help for the `genre` attribute of books, allowing administrators to select valid genre values from a list when creating policies in the AMS admin console.

## @valueHelp annotation in schema.dcl

In your `schema.dcl` file, add the `@valueHelp` annotation to the attribute you want to enable value help for. The `path` property defines the OData entity path that the AMS server will call on your value help service. The `valueField` and `labelField` properties specify which OData response properties to use as the value and human-readable label respectively.

```dcl
@valueHelp: {
path: 'Genres',
valueField: 'name',
labelField: 'name'
}
genre: String
```

## Value help policy in ams base policies

A base policy is required in `cap/basePolicies.dcl` that administrators can assign to users who need access to the value help endpoint:

```dcl
POLICY ValueHelpReader {
ASSIGN ROLE ValueHelpReader;
}
```

Additionally, create the file `internal/AMS_ValueHelp.dcl` containing an internal policy that grants the corresponding privileges to the AMS server when it calls the value help endpoint. The name of this `INTERNAL POLICY` **must exactly match** the `value-help-api-name` configured in `mta.yaml`. This is because `value-help-api-name` becomes the `ias_apis` claim value in the app2app token that the AMS server presents to your application. The `App2appAttributesProcessor` then maps that claim to `internal.AMS_ValueHelp`, and this policy determines what authorizations are granted:

```dcl
INTERNAL POLICY AMS_ValueHelp {
USE cap.ValueHelpReader;
}
```

## ValueHelpService

In parallel to the CDS files for the admin service and the catalog service, add a dedicated `.cds` file for the value help service. This service exposes the entities that provide value help data and must be protected so that only the AMS server (acting on behalf of an authorized administrator) can call it. Protect it with the `ValueHelpReader` role:

```cds
using { sap.capire.bookshop as my } from '../db/schema';

service AmsValueHelpService @(requires: 'ValueHelpReader') {
@cds.localized: false
entity Genres as projection on my.Genres;
}
```

The service name `AmsValueHelpService` is what determines the OData path `/odata/v4/AmsValueHelpService/`. The `value-help-url` in `mta.yaml` must point to the actual URL where the service is served — the names do not have to be identical, but the URL must be consistent with whatever CAP derives from the service name.

## App2App attributes processor

When the AMS server calls the value help endpoint, it uses a **principal propagation** token on behalf of the administrator who triggered the value help request. This token contains an `ias_apis` claim listing the API permission groups the AMS server is authorized to consume (e.g. `["AMS_ValueHelp"]`).

The `App2appAttributesProcessor` is a Java SPI implementation that intercepts this token and maps each API permission group to DCL policies via `PolicyAssignmentBuilder`. It uses explicit whitelists to control which APIs are accepted in which flow:

- **`TECHNICAL_USER_APIS`** — APIs allowed for pure technical user calls (no forwarded user). Each API is mapped to `internal.<api>`, granting the authorizations defined in that `INTERNAL POLICY`.
- **`PRINCIPAL_PROPAGATION_APIS`** — APIs allowed for principal propagation calls (forwarded user). Each API is mapped to `internal.<api>` as an upper-limit filter on what the calling application can do on behalf of the forwarded user.

In this sample both sets contain only `AMS_ValueHelp`, which maps to the `INTERNAL POLICY AMS_ValueHelp` in the `internal` DCL package (see `srv/src/main/resources/ams/internal/AMS_ValueHelp.dcl`). That policy uses `cap.ValueHelpReader`, which grants the `ValueHelpReader` role — satisfying the `@(requires: 'ValueHelpReader')` check on `AmsValueHelpService`.

To add support for additional APIs in your own application, add their names to the appropriate set. Any API not listed in either set is ignored even if it appears in the token, which prevents unintended access from unknown callers.

To register the processor, create the file `src/main/resources/META-INF/services/com.sap.cloud.security.ams.api.AttributesProcessor` containing the fully qualified class name of your implementation:

```
customer.cap_java_vh.App2appAttributesProcessor
```

## Configuration in mta.yaml

Add `provided-apis` and an `authorization` block to the `config` section of the IAS (`identity`) service resource. The `value-help-url` **must use the `.cert.` domain** — on Cloud Foundry.

```yaml
resources:
- name: sample-cap-java-vh-auth
type: org.cloudfoundry.managed-service
parameters:
service: identity
service-plan: application
config:
provided-apis:
- name: AMS_ValueHelp
description: Value Help Callback from AMS
type: public
authorization:
enabled: true
value-help-url: ~{srv-api/srv-cert-url}/odata/v4/AmsValueHelpService/
value-help-api-name: AMS_ValueHelp
```

> **Important:** The `consumed-services` entry on the AMS admin console's IAS application — which allows the AMS server to request an app2app token targeting your app — is only registered by the IAS service broker at service instance **creation** time. If you add value help configuration to an existing deployment, a simple redeploy is not sufficient. You must delete the service instance and redeploy so that a fresh instance is created:
> ```sh
> cf delete-service <your-auth-service-name> -f
> cf deploy <your-mtar-file>
> ```

15 changes: 15 additions & 0 deletions ams-value-help-sample/app/_i18n/i18n.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Books = Books
Book = Book
ID = ID
Title = Title
Author = Author
Authors = Authors
AuthorID = Author ID
AuthorName = Author Name
Name = Name
Age = Age
Stock = Stock
Order = Order
Orders = Orders
Price = Price
Genre = Genre
15 changes: 15 additions & 0 deletions ams-value-help-sample/app/_i18n/i18n_de.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Books = Bücher
Book = Buch
ID = ID
Title = Titel
Author = Autor
Authors = Autoren
AuthorID = ID des Autors
AuthorName = Name des Autors
Name = Name
Age = Alter
Stock = Bestand
Order = Bestellung
Orders = Bestellungen
Price = Preis
Genre = Genre
113 changes: 113 additions & 0 deletions ams-value-help-sample/app/admin-books/fiori-service.cds
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using {AdminService} from '../../srv/admin-service.cds';

////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate AdminService.Books with @(UI: {
HeaderInfo : {
TypeName : '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title : {Value: title},
Description : {Value: author.name}
},
Facets : [
{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>General}',
Target: '@UI.FieldGroup#General'
},
{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>Translations}',
Target: 'texts/@UI.LineItem'
},
{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>Details}',
Target: '@UI.FieldGroup#Details'
},
{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>Admin}',
Target: '@UI.FieldGroup#Admin'
}
],
FieldGroup #General: {Data: [
{Value: title},
{Value: author_ID},
{Value: genre_ID},
{Value: descr}
]},
FieldGroup #Details: {Data: [
{Value: stock},
{Value: price}
]},
FieldGroup #Admin : {Data: [
{Value: createdBy},
{Value: createdAt},
{Value: modifiedBy},
{Value: modifiedAt}
]}
});


////////////////////////////////////////////////////////////
//
// Draft for Localized Data
//
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
annotate AdminService.Books with @odata.draft.enabled;

annotate AdminService.Books.texts with @(UI: {
Identification : [{Value: title}],
SelectionFields: [
locale,
title
],
LineItem : [
{
Value: locale,
Label: 'Locale'
},
{
Value: title,
Label: 'Title'
},
{
Value: descr,
Label: 'Description'
}
]
});

annotate AdminService.Books.texts with {
ID @UI.Hidden;
ID_texts @UI.Hidden;
};

// Add Value Help for Locales
annotate AdminService.Books.texts {
locale @(
ValueList.entity: 'Languages',
Common.ValueListWithFixedValues //show as drop down, not a dialog
)
};

// In addition we need to expose Languages through AdminService as a target for ValueList
using {sap} from '@sap/cds/common';

extend service AdminService {
@readonly
entity Languages as projection on sap.common.Languages;
}

// Workaround for Fiori popup for asking user to enter a new UUID on Create
annotate AdminService.Books with {
ID @Core.Computed;
}

// Show Genre as drop down, not a dialog
annotate AdminService.Books with {
genre @Common.ValueListWithFixedValues;
}
8 changes: 8 additions & 0 deletions ams-value-help-sample/app/admin-books/webapp/Component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sap.ui.define(["sap/fe/core/AppComponent"], function (AppComponent) {
"use strict";
return AppComponent.extend("books.Component", {
metadata: { manifest: "json" }
});
});

/* eslint no-undef:0 */
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appTitle=Manage Books
appSubTitle=Manage bookshop inventory
appDescription=Manage your bookshop inventory with ease.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appTitle=Bücher verwalten
appSubTitle=Verwalten Sie den Bestand der Buchhandlungen
appDescription=Verwalten Sie den Bestand Ihrer Buchhandlung ganz einfach.
Loading
Loading