WORK IN PROGRESS
...
& For Discussion
Background
Some A1-Policy operations require the creator (and owner) of an A1-Policy to be known.
In the pre-R1 A1-Policy API it was required to provide a 'service_id' to link A1-Policies to the client or service that 'owns' them.
- https://docs.onap.org/projects/onap-ccsdk-oran/en/latest/offeredapis/pms-api.html#tag/Service-Registry-and-Supervision
- https://docs.onap.org/projects/onap-ccsdk-oran/en/latest/offeredapis/pms-api.html#tag/A1-Policy-Management/operation/putPolicy
Where this mandatory 'service_id' is a registered 'Service', then the new A1-Policy instance is subject to the 'keep-alive' requirements for the service, and policies are deleted when the 'Service' is unregistered/deleted.
However, this mandatory 'service_id' did not need to correspond to an existing registered 'Service', and if the value corresponded to an unregistered 'Service' service registration is not performed for that service.
A "Service" cannot be registered with a null or empty (""), however an empty value "" can be passed for 'service_id' when creating a policy - to represent "no service".
O-RAN Alliance R1-GAP, R1-AP and Security specifications describe how R1 requests authentication headers should include a bearer-token, which includes the 'ClientID' of the R1-Service invoker. There is an assumption (TBC) that this is the 'rAppID' for R1 invocations by rApps. When creating A1-Polices this 'ClientID' corresponds to the 'owner' and 'creator' of those A1-Policies. ('owner' == 'creator').
When a 'ClientID' is present in a the Auth header of a request, as part of a cryptographically signed/verified JWT token, the 'ClientID' will be the actual service invoker. JWT tokens generally should not be shared, and it should not be possible for one client to masquerade as a different client.
However, there may be cases where non-rApp clients need to be 'creator' for an A1-Policy, but not the 'owner'. e.g. an admin client, a GUI, a batch or restore operation, distributed applications, etc.. So it should be possible to also include supplementary 'owner' information in an A1-Policy create request.
In the R1-aligned enhanced ('v3') API introduced for A1-PMS (link currently broken: https://docs.onap.org/projects/onap-ccsdk-oran/en/latest/offeredapis/v3/pms-api-v3.html) the Create-Policy operation retained the 'service_id' parameter as an optional non-spec parameter, with default value as empty string "". In this way we can use either/both 'ClientID' in the header's JWT token and/or the 'service_id' parameter in the create-policy operation.
This page discusses how to deal with combinations of 'ClientID' (as "creator" and/or "owner") in the Header, and 'service_id' ( as "owner") in the request body.
Scenarios:
Info | ||
---|---|---|
| ||
The discussion below does not consider whether a Create-Policy request (or any other request) should be allowed or not.
Therefore, depending on how the service is deployed and configured, some of the scenarios below may be restricted, and the operation may fail with an 'Unauthorized' error. |
Original pre-spec v2 API:
- Request 'service_id' ❌ absent (null)
Token or its 'ClientID' ❌ absent (null), or
Token's 'ClientID' ✔️ present but blank/empty (""), or
Token's 'ClientID' ✅present
Panel border true Leave parameter 'service_id' as null and continue.
Operation will fail because 'service_id' is mandatory.
- Request 'service_id' ✔️ present but blank/empty ("")
Token or its 'ClientID' ❌ absent (null)
Panel border true Leave parameter 'service_id' as empty ("") and continue.
This policy has no 'owner', so cannot be retrieved using a service-id filter in Get-Instances, and cannot be later inherited by a newly-registered service ("" is not a valid service name when registering a 'Service').
Policy cannot be auto-deleted/garbage-collected, since this functionality is tried to the life-cycle of registered Services only.
Token's 'ClientID' ✔️ present but blank/empty ("")
Panel border true Leave parameter 'service_id' as empty ("") and continue.
This policy has no 'owner', so cannot be retrieved using a service-id filter in Get-Instances, and cannot be later inherited by a newly-registered service ("" is not a valid service name when registering a 'Service').
Policy cannot be auto-deleted/garbage-collected, since this functionality is tried to the life-cycle of registered Services only.
Token's 'ClientID' ✅present
Panel border true Set parameter 'service_id' = 'ClientID' and continue.
This policy's 'owner' is set be its 'creator'.
See options 3.d and 4.d below to understand behavior when 'service_id' == 'ClientID'
- Request 'service_id' ✅present but not a registered 'Service'
Token or its 'ClientID' ❌ absent (null)
Panel border true Leave parameter 'service_id' as is (not-registered Service) and continue.
This policy has an unregistered 'owner', but can be retrieved using that 'service-id' filter in Get-Instances
Policy cannot be auto-deleted/garbage-collected, since this functionality is tried to the life-cycle of registered Services only.
Policy can be later inherited by a newly-registered Service with same 'service_id'.Token's 'ClientID' ✔️ present but blank/empty ("")
Panel border true Leave parameter 'service_id' as is (not-registered Service) and continue.
This policy has an unregistered 'owner', but can be retrieved using that 'service-id' filter in Get-Instances
Policy cannot be auto-deleted/garbage-collected, since this functionality is tried to the life-cycle of registered Services only.
Policy can be later inherited by a newly-registered Service with same 'service_id'.Token's 'ClientID' ✅present but != 'service_id'
Panel border true Leave parameter 'service_id' as is (not-registered Service) and continue.
Policy has an different 'owner' than 'creator'This policy has an unregistered 'owner', but can be retrieved using that 'service-id' filter in Get-Instances
Policy cannot be auto-deleted/garbage-collected, since this functionality is tried to the life-cycle of registered Services only.
Policy can be later inherited by a newly-registered Service with same 'service_id' (see option 4.c below).Warning title To Do! Need to extend functionality to save the additional 'creator' information.
Token's 'ClientID' ✅present and == 'service_id'
Panel border true Leave parameter 'service_id' as is (not-registered Service) and continue.
This policy has an unregistered 'owner', but can be retrieved using that 'service-id' filter in Get-Instances
Policy cannot be auto-deleted/garbage-collected, since this functionality is tried to the life-cycle of registered Services only.
Policy can be later inherited by a newly-registered Service with same 'service_id' (see option 4.d below).
- Request 'service_id' ✅present but is a registered 'Service'
Token or its 'ClientID' ❌ absent (null)
Panel border true Leave parameter 'service_id' as is (pre-registered Service) and continue.
This policy has a registered 'owner', and can be retrieved using that 'service-id' filter in Get-Instances
Policy may be auto-deleted/garbage-collected, depending on the life-cycle and activity registered Service.
Policy will be deleted if Service is deleted or times-out according to however the Service is configured.Token's 'ClientID' ✔️ present but blank/empty ("")
Panel border true Leave parameter 'service_id' as is (pre-registered Service) and continue.
This policy has a registered 'owner', and can be retrieved using that 'service-id' filter in Get-Instances
Policy may be auto-deleted/garbage-collected, depending on the life-cycle and activity registered Service.
Policy will be deleted if Service is deleted or times-out according to however the Service is configured.Token's 'ClientID' ✅present but != 'service_id'
Panel border true Leave parameter 'service_id' as is (pre-registered Service) and continue.
Policy has an different 'owner' than 'creator'This policy has a registered 'owner', and can be retrieved using that 'service-id' filter in Get-Instances
Policy may be auto-deleted/garbage-collected, depending on the life-cycle and activity registered Service.
Policy will be deleted if Service is deleted or times-out according to however the Service is configured.Warning title To Do! Need to extend functionality to save the additional 'creator' information.
Token's 'ClientID' ✅present and == 'service_id'
Panel border true Leave parameter 'service_id' as is (pre-registered Service) and continue.
Policy has same 'owner' than 'creator'This policy has a registered 'owner' - same as 'creator', and can be retrieved using that 'service-id' filter in Get-Instances
Policy may be auto-deleted/garbage-collected, depending on the life-cycle and activity registered Service.
Policy will be deleted if Service is deleted or times-out according to however the Service is configured.
New R1-spec v3 API:
The main difference between 'v2' API and new 'v3' API is that 'service_id' is now optional instead of mandatory.
So all scenarios are the same as above EXCEPT scenario #1
Request 'service_id' ❌ absent (null)
Panel border true Set parameter 'service_id' as empty ("") and continue.
See option set #2 above
Demo with tokens and ClientID extraction for using it as service_id
...
Integrating with Existing Code: In the codebase, there is already a function to get the token located in the class org.onap.ccsdk.oran.a1policymanagementservice.controllers.authorization.AuthorizationCheck
...
To generate a JWT token and parse a value from it demonstration.
Below, I'll outline the steps to achieve this, including generating a JWT, sending it in a request header, and then parsing a value (like `client_id`) from the JWT payload.
...
$ ([DateTime]('1970,1,1')).AddSeconds(3000000000)
24 January 2065 05:20:00
Sending the JWT in a REST Request Header
You can use curl to send the JWT in a request header:
Code Block | ||||
---|---|---|---|---|
| ||||
curl -H "Authorization: Bearer $jwt" http://A1PMS/policy.. |
Parsing the JWT Payload in Java
Here's an example of how to parse the `client_id` from the JWT in Java:
Code Block | ||||
---|---|---|---|---|
| ||||
import java.util.Base64;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class ParseJWT {
public String parseServiceId(String token) {
// Split token into its parts
String[] chunks = token.split("\\.");
Base64.Decoder decoder = Base64.getUrlDecoder();
// Decode payload
String payload = new String(decoder.decode(chunks[1]));
// Parse JSON using Gson
JsonObject jsonObject = JsonParser.parseString(payload).getAsJsonObject();
// Extract the client_id
String clientId = jsonObject.get("client_id").getAsString();
return clientId;
}
} |
...
Policy Creation Scenario
In this example I want to log the Bearer Token given to the call: PUT
In Postman I use the generated token I got from the bash script as Bearer Token:
It would be equivalent to:
...