Développer des fonctions
Avant de déployer une passerelle d'API, vous devez développer et déployer vos fonctions.
A propos de la logique applicative
Le chemin de l'implémentation consiste à placer du code dans une fonction. Ce code peut être aussi complexe que nécessaire, en appelant plusieurs adresses ou en exécutant éventuellement une agrégation. La fonction de logique applicative est le code qui sera appelé en cas de besoin par la passerelle d'API Oracle Cloud Infrastructure.
Dans cet exemple d'architecture, API Gateway appelle une fonction Oracle qui interroge ensuite certaines données d'Oracle Fusion Applications Cloud Service via l'API REST, les manipule et les renvoie à l'utilisateur. Lorsque vous écrivez la fonction elle-même, vous pouvez utiliser n'importe quelle structure adaptée, mais vous devez connaître l'impact d'une structure pour les fonctions sans serveur. Dans cet exemple, nous avons utilisé Java comme langue et la bibliothèque Apache HttpClient pour nous connecter au service REST. La bibliothèque Apache a été choisie car elle est facile à utiliser et facile à implémenter. Toutefois, dans Java 11, nous disposons désormais du nouveau client HTTP qui pourrait également être utilisé.
Evitez également les structures qui instancient de nombreux objets en mémoire lors de l'appel d'API REST, car ces objets seront ignorés à chaque appel et, par conséquent, ralentissent l'exécution de la fonction.
A propos de l'obtention de noms utilisateur et de rôles
Lorsqu'une fonction est appelée par une passerelle d'API Oracle Cloud Infrastructure, la passerelle injecte des métadonnées dans l'appel à l'aide d'en-têtes HTTP. Ces en-têtes sont accessibles à l'aide du kit SDK de fonctions.
Par exemple, dans le fragment de code Java suivant (extrait de la classe JWTUtils
dans l'exemple de code fourni avec ce livre de lecture de solution), nous extrayons le jeton d'authentification, le décodage, puis le renvoyons dans le corps de l'appelant.
public OutputEvent handleRequest(InputEvent rawInput) {
Optional<string> optionalToken=rawInput.getHeaders().get("Fn-Http-H-Authorization");
if (!optionalToken.isPresent())
{
throw new Exception("No Authentication Bearer token found");
}
String jwtToken=optionalToken.get().substring(TOKEN_BEARER_PREFIX.length());
String[] split_string = jwtToken.split("\\.");
String base64EncodedHeader = split_string[0];
String base64EncodedBody = split_string[1];
String base64EncodedSignature = split_string[2];
byte[] decodedJWT = Base64.getDecoder().decode(base64EncodedBody);
try {
String JSONbody = new String(decodedJWT, "utf-8");
ObjectMapper mapper = new ObjectMapper();
JsonNode root=mapper.readTree(JSONbody);
username=root.get("sub").asText();
System.out.println(“Username = “+username);
} catch (Exception e)
{
Throw new Exception (e.getMessage());
}
Return OutputEvent.fromBytes(
username.getBytes(), // Data
OutputEvent.Status.Success,// Any numeric HTTP status code can be used here
"text/plain");
Le nom utilisateur peut être employé dans la fonction pour implémenter la logique métier en fonction des besoins. Les rôles ne sont pas disponibles dans le jeton d'authentification, mais ils peuvent être interrogés à partir d'Oracle Identity Cloud Service à l'aide des API REST.
Lorsqu 'Oracle API Gateway appelle la fonction, de nombreux en-têtes utiles sont également envoyés. Ces en-têtes peuvent être lus à l'aide de l'API de la fonction. Les en-têtes utiles disponibles dans le kit de développement des fonctions sont les suivants :
Fn-Http-Method | Méthode utilisée pour appeler la fonction (GET/POST/PUT, etc.). |
Fn-Http-Request-Url | URL utilisée pour appeler la fonction, qui inclut les paramètres de requête. |
Fn-Http-H-User-Agent | Détails du client, tels que le système d'exploitation, le fournisseur et la version. |
Par exemple, si vous utilisez une combinaison Fn-Http-Method et Fn-Http-Request-Url, vous pouvez implémenter un routeur dans votre code de sorte que votre fonction ait différentes choses en fonction de son nom (comme PUT, PATCH, etc.). Cette approche est souvent appelée "modèle de service sans serveur” et présente l'avantage que le développeur doit gérer moins de fonctions et que chaque fonction a un peu routeur qui détermine ce qu'il doit faire.
public OutputEvent handleRequest(InputEvent rawInput, HTTPGatewayContext hctx) throws JsonProcessingException {
String httpMethod = hctx.getMethod();
String httpRequestURI = hctx.getRequestURL();
// etc
}
A propos de l'appel d'Oracle Fusion Applications Cloud Service à l'aide d'un jeton d'accès d'assertion JWT
Vous devez implémenter l'assertion JWT à partir d'Oracle Functions en utilisant le sujet dans l'en-tête d'autorisation entrant.
L'exemple de fonction fourni avec ce guide de lecture de solution peut effectuer ce processus de sécurité à l'aide de la bibliothèque helper nommée idcsOAuthAsserter
. La bibliothèque helper exécute l'assertion OAuth complète en échangeant des jetons de porteur avant d'appeler Oracle Fusion Applications Cloud Service. Cette bibliothèque est intégrée à l'exemple de fonction.
La fonction exige la clé privée et le certificat public pour créer les assertions utilisateur et client utilisées pour appeler Oracle Identity Cloud Service afin de créer un jeton d'accès de service support à l'aide de l'assertion JWT OAuth.
La bibliothèque idcsOAuthAsserter
nécessite des propriétés que vous pouvez définir dans la configuration des fonctions. Toutes les variables figurant dans le tableau suivant sont obligatoires :
Nom de configuration | Description | Exemple |
---|---|---|
IDCS_URL | URL de votre instance Oracle Identity Cloud Service | https://<votre cloud d'identité hostname.identity.oraclecloud.com> |
CLIENT_ID | Votre ID de client d'application Oracle Identity Cloud Service associé à Oracle Functions et Oracle API Gateway | 1a2b3c4d5e6f7g8h9i01j2k3l4m5o6p7 |
KEY_ID | Alias des certificats importés dans l'application Trusted Oracle Identity Cloud Service | fnassertionkey |
ETENDUE | Cette portée doit correspondre à la ressource OAuth cible, qui est l'application Oracle Identity Cloud Service associée à votre Oracle Fusion Applications Cloud Service | urn:opc:resource:fa:instanceid=xxxxxxxxxurn:opc:resource:consumer : : all https://my_fusion_hostname:443/ |
PUBLIC VISÉ | Publics du processus d'assertion. S'il existe plusieurs valeurs, séparez-les par une virgule. | myhostname.identity.oraclecloud.com, https://myfusionservice.dc1.oraclecloud.com |
IDDOMAIN | Nom du locataire d'instance Oracle Fusion Applications Cloud Service | mytenant |
Cette fonction exigera également des propriétés de configuration pour accéder aux secrets des assertions relatives à idcsOAuthAsserter
. L'assertion JWT requiert un certificat et une clé privée pour générer les assertions client et utilisateur. La fonction extrait le fichier de clés avec l'OCID indiqué dans V_KEYSTORE
. L'alias permettant d'extraire ces informations doit correspondre à la valeur KEY_ID
dans la configuration. La phrase de passe du fichier de clés et de la clé privée doit être extraite du service de sécurité Oracle Cloud Infrastructure Vault à l'aide des identificateurs d'hôte indiqués dans V_KS_PASS
et V_PK_PASS
.
Nom de configuration | Description | Exemple |
---|---|---|
V_KEYSTORE | Clé secrète qui contient le contenu stocké sécurisé du fichier de clés | ocid1.vaultsecret.oc1.dc1. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
V_KS_PASS | Clé secrète contenant le mot de passe sécurisé stocké pour le fichier de clés | ocid1.vaultsecret.oc1.dc1. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
V_PK_PASS | Clé secrète contenant le mot de passe stocké sécurisé pour la clé privée | ocid1.vaultsecret.oc1.dc1. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
La propriété USE_CACHE_TOKEN
, qui est définie par défaut sur True
, représente une valeur de configuration supplémentaire prise en charge. Cette propriété vous permet de stocker le jeton d'assertion Oracle Identity Cloud Service généré pour être réutilisé dans les futurs appels d'Oracle Fusion Applications Cloud Service, tant que le jeton reste valide. Le jeton en mémoire cache est validé avant utilisation en comparant le sujet avec le jeton entrant et en vérifiant le délai d'expiration pour vérifier s'il est toujours valide. Sinon, un nouveau jeton d'accès est demandé à l'aide de l'assertion OAuth. Cette fonctionnalité peut être désactivée en définissant USE_CACHE_TOKEN
sur False
.
La fonction à implémenter peut utiliser SecurityHelper object
à partir de la bibliothèque idcsOAuthAsserter
pour extraire le sujet à partir de l'en-tête access-token
, générer un nouveau jeton d'accès de service support avec l'assertion JWT OAuth et envoyer une demande à Oracle Fusion Applications Cloud Service en utilisant le nouveau jeton d'accès comme en-tête Authorization
.
La fonction saasopportunitiesfn
dans l'exemple de code source est déjà intégrée à la bibliothèque idcsOAuthAsserter
. Le fragment de code suivant est disponible dans la méthode metdhod handleRequest
de l'exemple de fonction, qui montre comment initialiser les objets de idcsOAuthAsserter
et comment effectuer une modification de sèmes à l'aide d'une assertion avec Oracle Identity Cloud Service :
// Full Oauth scenario Perform exchange of tokens
if(fullOAauth) {
LOGGER.log(Level.INFO, "Full Oauth Assertion scenario - Perform exchange of tokens");
SecurityHelper idcsSecurityHelper = new SecurityHelper(context) // Initialize SecurityHelper with RuntimeContext
.setOciRegion(Region.US_PHOENIX_1) // Specify the OCI region, used to retrieve Secrets.
.extractSubFromJwtTokenHeader(rawInput); // Extracts the subject from Token in Fn-Http-H-Authorization.
// Get OAuth Access token with JWT Assertion using the principal extracted from Fn-Http-H-Access-Token Header
jwttoken = idcsSecurityHelper.getAssertedAccessToken();
LOGGER.log(Level.INFO, "Successfully token retrived with IDCS Assertion");
LOGGER.log(Level.FINEST, "Access Token from assertion [" + jwttoken + "]");
}
Dans ce fragment de code, l'objet SecurityHelper
est initialisé avec un objet de contexte de type RuntimeContext
. Cela permettra d'initialiser l'élément SecurityHelper
avec la configuration de la bibliothèque Helper idcsOAuthAsserter
.
L'exemple de fonction saasopportunitiesfn
est défini pour utiliser la région US_PHOENIX_1
. Par conséquent, vous devez modifier la région dans la méthode setOciRegion
affichée dans l'extrait pour qu'elle pointe vers votre région.
SecurityHelper
possède également la méthode extractSubFromJwtTokenHeader
qui prend l'objet InputEvent
de la méthode handleRequest
Function pour extraire le jeton Bearer qui provient de l'en-tête API Gateway Authorization
. Vous devez ensuite pouvoir extraire un jeton d'accès suite à l'assertion Oracle Identity Cloud Service.
Pour plus d'informations sur l'utilisation et l'intégration de idcsOAuthAsserter
avec une fonction, consultez le fichier README de idcsOAuthAsserter
dans le référentiel de codes avec l'exemple de code téléchargeable associé à cette solution playbook.
Définir les paramètres de configuration
L'environnement Oracle Functions fournit une fonctionnalité très utile pour définir des paramètres dans l'environnement Oracle Cloud Infrastructure, puis les référencer à partir de votre code.
Dans ce cas, vous allez définir les URL de jeton OverrideJWT et Fusion, ainsi qu'un indicateur nommé full_oauth
, en tant que paramètres utilisés dans votre code. Pour ajouter des paramètres :
Dans le code, vous pouvez accéder à ces variables de configuration à l'aide du kit SDK Functions en utilisant une annotation Java spéciale (@FnConfiguration
) et en accédant aux paramètres via la variable de contexte :
private String jwtoverride = "";
private String fusionHostname = "";
private String fnURIBase ="/fnsaaspoc/opportunities";
/**
* @param ctx : Runtime context passed in by Fn
*/
@FnConfiguration
public void config(RuntimeContext ctx) {
fusionHostname = ctx.getConfigurationByKey("fusionhost").orElse("NOTSET");
jwtoverride = ctx.getConfigurationByKey("overridejwt").orElse("NOTSET");
fullOAauth = Boolean.parseBoolean(ctx.getConfigurationByKey("full_oauth").orElse("false"));
LOGGER.info("Configuration read : jwt=" + jwtoverride + " saasurl=" + fusionHostname);
}
En outre, étant donné que cette solution utilise la bibliothèque helper idcsOAuthAsserter
, vous devez fournir dans la configuration de fonction les variables spécifiques décrites dans la section précédente pour utiliser l'assertion OAuth Oracle Identity Cloud Service pour échanger les jetons d'accès. Etant donné que ce processus requiert plusieurs configurations obligatoires, nous vous conseillons d'utiliser l'approche de fichier yaml pour configurer votre fonction. Par exemple :
config:
AUDIENCE: <AUDIENCE_VALUES>
CLIENT_ID: <YOUR_CLIENT_ID>
IDCS_URL: <YOUR_IDCS_URL>
IDDOMAIN: <YOUR_FA_TENANT_NAME>
KEY_ID: <YOUR_IDCS_URL>
SCOPE: <FA_RESOURCE_SCOPE>
V_KEYSTORE: <YOUR_KS_OCID>
V_KS_PASS: <YOUR_KSPASS_OCID>
V_PK_PASS: <YOUR_PKPASS_OCID>
fusionhost: <value>
overridejwt: <value>
full_oauth: <value>
Transmettre le jeton d'authentification utilisateur à Fusion Applications
Votre fonction doit gérer le jeton d'authentification utilisateur pour les interactions sécurisées de l'API REST.
Lorsque la fonction est appelée, elle reçoit également une variable d'en-tête “autorisation” contenant un jeton d'authentification. Ce jeton est généré par le serveur d'identité de l'application appelante, qui est le même serveur d'identité que celui utilisé par la fonction de passerelle d'API Oracle Cloud Infrastructure pour valider la demande.
L'approche décrite dans cette solution playbook consiste à utiliser l'assertion JWT OAuth Oracle Identity Cloud Service pour effectuer l'échange de jetons à l'aide de la bibliothèque helper idcsOAuthAsserter
. Vous obtenez ainsi une couche de sécurité supplémentaire pour l'appel d'Oracle Functions vers Oracle Fusion Applications Cloud Service. Comme indiqué dans le diagramme d'architecture, la fonction avec des requêtes d'assertion JWT OAuth pour le jeton qui provient de l'appel entrant de l'en-tête de passerelle d'API et l'utilise pour être modifiée pour un autre jeton lors du processus d'assertion avec Oracle Identity Cloud Service. Ce nouveau jeton sera utilisé lors de l'appel sortant vers votre serveur de destination ( Oracle Fusion Applications Cloud Service ) et ( Oracle Fusion Applications Cloud Service ) exécutera l'appel sous le nom utilisateur dans Oracle Visual Builder.
Dans l'exemple de fonction fourni (saasopportunitiesfn
), il existe une configuration nommée full_oauth
, qui est définie par défaut sur True
et le comportement sera décrit ci-dessus.
Vous pouvez également définir full_oauth
sur False
. Dans ce cas, la fonction interroge le jeton qui provient de l'appel entrant de l'en-tête de passerelle d'API et le réutilise dans l'appel sortant vers Oracle Fusion Applications Cloud Service. Aucun second jeton n'est généré.
Le fragment de code suivant utilise la bibliothèque Apache HTTP et appelle l'API REST Oracle Fusion Applications Cloud Service à l'aide du jeton d'authentification transmis.
String fusionURL=”<yourfusionresturl>”;
HttpClient client = HttpClients.custom().build();
HttpUriRequest request = RequestBuilder.get().setUri(fusionURL).
setHeader(HttpHeaders.CONTENT_TYPE, "application/json").
setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + jwtToken).
build();
HttpResponse response = client.execute(request);
responseJson = EntityUtils.toString(response.getEntity());
status = response.getStatusLine().getStatusCode();
Compiler et déployer votre fonction
Dans Oracle Functions, créez une application pour votre fonction, puis déployez la fonction.
Définir éventuellement une fonction d'authentification dans Oracle Cloud Infrastructure
La passerelle d'API Oracle Cloud Infrastructure prend en charge de façon native IDCS en tant que fournisseur d'authentification. Cependant, la passerelle permet également la définition d'une fonction qu'elle peut appeler. Vous pouvez éventuellement créer une fonction d'authentification personnalisée à l'aide de cette fonctionnalité.
La fonction d'authentification personnalisée reçoit un appel de la passerelle, en le transmettant à l'en-tête d'autorisation entrant. Si la fonction renvoie la valeur True, l'appel de fonction est autorisé. S'il renvoie la valeur False, la demande est rejetée avec un code d'erreur HTTP 401. La fonction est fournie au format de code source et est déployée dans Fonctions, qui est ensuite référencée dans le fichier de déploiement GATEWAY de l'API OCI via son OCID. Vous pouvez repérer l'OCID en accédant à la fonction déployée dans la console et en développant sa colonne OCID. Avant de déployer la fonction, vous devez modifier le fichier ResourceServerConfig.java
; cela définit le mode de connexion de la fonction à Oracle Identity Cloud Service et l'application Oracle Identity Cloud Service OAuth utilisée.
L'exemple de fonction ci-dessous permet d'éviter le codage permanent des valeurs comme la clé secrète du client. Dans les quatre lignes en dessous du commentaire // KMS Key for IDCS Client Secret
, le code décrypte la clé secrète OAuth à l'aide de la fonctionnalité de gestion de clés Oracle Cloud Infrastructure, Oracle Cloud Infrastructure Vault. Saisissez ces valeurs dans la console d'Oracle Cloud Infrastructure avant de déployer la fonction.