El blog «Integración en la nube: conéctese a Microsoft 365 Mail con OAuth2contiene capítulos sobre cómo puede conectarse desde SAP Cloud Integration a Microsoft 365 a través de OAuth2 para enviar correos utilizando el protocolo SMTP. En este blog describimos una alternativa al protocolo SMTP: usamos Microsoft Graph DESCANSAR API para enviar correos utilizando el adaptador de receptor http de SAP Cloud Integration.
Creamos un flujo de integración que se conecta a la API REST de Microsoft Graph descrita en https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http.
Al conectarse a Outlook 365 con OAuth2, debe tener un directorio organizativo/inquilino en Microsoft Azure Active Directory y un usuario en este directorio que tenga una suscripción a Outlook 365. La siguiente captura de pantalla muestra un ejemplo de dicho usuario en Azure Active Directory con nombre “testusermail” que tiene la licencia “Exchange Online (Plan1)”.
Puede comprobar si el usuario tiene una suscripción a Outlook 365 iniciando sesión con el usuario en https://outlook.office365.com/mail/.
Para las tareas de configuración en Azure Active Directory, también necesita un usuario con el rol de «Administrador de aplicaciones» y «Desarrollador de aplicaciones».
Además, necesita un inquilino de SAP Cloud Integration o Integration Suite en el que tenga un usuario con el rol de «Desarrollador de integración».
Creamos un flujo de integración al que puede llamar a través de un cliente HTTP y que se conecta a Microsoft Graph a través del adaptador receptor HTTP de SAP Cloud Integration. Para configurar este escenario, ejecute los siguientes pasos:
Cuando inicia sesión en SAP Cloud Integration o Integration Suite WEB-UI, ve su nombre de host en el campo de dirección del navegador:
https://<host name>/itspaces (for Cloud Integration)
https://<host name>/shell/home (for Integration Suite)
Utilice el
Necesita este URI de redireccionamiento en el siguiente paso.
Guarde la ID de la aplicación (cliente) en cualquier lugar de su escritorio local. Necesitará este ID más tarde para configurar la credencial OAuth2 en CPI.
4. Elija «Certificados y secretos» en el menú de la izquierda.
5. Selecciona “Nuevo secreto de cliente”, elige el periodo de caducidad que prefieras (“En 1 año”, “En 2 años” o “Nunca”). Opcionalmente, también puede agregar una descripción. Cuando haya terminado, seleccione «Agregar».
Observación: antes de que caduque el secreto, debe crear un nuevo secreto y transferir el nuevo secreto a la credencial del código de autorización SAP CPI OAuth2 (consulte a continuación).
6. Use el botón «Copiar al portapapeles» para recordar el secreto creado (lo necesitará más adelante para configurar la credencial OAuth2 en CPI).
7. Vuelva a la vista «Descripción general» de la aplicación y seleccione la pestaña «Puntos finales».
Copie el «punto final de autorización de OAuth 2.0 (v2)» y el «punto final de token de OAuth 2.0 (v2)» en su escritorio local. Necesitará estos valores más adelante para la creación de la credencial OAuth2 en Cloud Integration.
8. Elige “Permisos API” en el menú de la izquierda y comprueba que está configurado el permiso “Microsoft Graph User.Read”. Este permiso debería estar ahí por defecto. Si no, entonces agréguelo.
Inicie sesión en su inquilino de Cloud Integration a través de la URL https://
Ingrese un nombre para la credencial y la «URL de autorización», «URL de servicio de token», «ID de cliente» y «Secreto de cliente» de su aplicación de Microsoft.
Introduzca también un «Nombre de usuario». Esta es la dirección de correo electrónico del usuario a cuyos recursos de correo desea acceder en un flujo de integración. Este usuario debe existir en el mismo directorio/inquilino de Microsoft Azure que la aplicación creada y debe tener una cuenta de Outlook 365.
Ingrese el alcance “https://graph.microsoft.com/Mail.Send”
Además, necesita el alcance «offline_access» para crear tokens de actualización (si no se agrega este alcance, SAP Cloud Integration agregará este alcance automáticamente). Los ámbitos deben estar separados por un espacio.
El valor predeterminado para el vencimiento del token de actualización se establece en 90 días para «Microsoft 365» (ver: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-configurable-token-lifetimes). Sin embargo, si se cambió el tiempo de vencimiento para su arrendatario de Microsoft, ajuste este valor.
Después de hacer clic en el botón «Implementar», verá la credencial «Código de autorización OAuth2» recién creada en la lista de Materiales de seguridad en estado «No autorizado“.
Seleccione los tres puntos en la entrada con la credencial creada y elija la opción «Autorizar».
Aparecerá una ventana emergente de confirmación. Selecciona “Continuar”:
Aparece una pantalla de inicio de sesión de Microsoft. Ingrese la contraseña del usuario que especificó en la credencial OAuth2:
Después de seleccionar «Iniciar sesión», aparece otra ventana emergente que indica los permisos solicitados que requiere la aplicación:
Seleccione «Aceptar». Debería recibir un mensaje de éxito:
Regrese a la página anterior del navegador y actualice la lista de Materiales de seguridad (botón «Recargar contenido»). El estado de la credencial «Código de autorización OAuth2» cambió a «Implementado»:
El siguiente guión de Groovy es la parte más importante de nuestro escenario. Groovy Script obtiene el token de acceso de OAuth2 del servidor de identidad de Microsoft y crea el cuerpo de la solicitud HTTP para enviarlo a Microsoft Graph.
Creamos una Colección de Scripts para que el Groovy Script pueda ser reutilizado en varios flujos de integración.
En la vista de diseño, donde también crea flujos de integración, puede crear una colección de scripts:
Seleccione la opción «Guión Groovy»:
Introduzca un nombre para Groovy Script.
En el lado derecho aparece un Groovy Script pregenerado que sobreescribimos en el siguiente paso.
Introduce en el Groovy Script Editor el siguiente código:
/** This script fetches an access token from the OAuth2 Authorization Code credential whose name is povided in the property "OAUTH2_AUTHORIZATION_CODE_CRED".
* sets the access token as Bearer token in the the haeder "Authorization",
* and creates a body in JSON format (as described in https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http)
* which can be sent to the Microsoft Graph API "https://graph.microsoft.com/v1.0/me/sendMail" via the HTTP receiver adapter.
*
* The incomming message body is used as mail body and it is assumed the the incmming message body has a charset encoding of UTF-8.
*
* The script is parametrized by the following properties:
* - "OAUTH2_AUTHORIZATION_CODE_CRED": name of the OAuth2 Authorization Code credential created before (in our example: "SEND_MAIL_VIA_GRAPH_API")
* - "MAIL_SAVE_TO_SENT_ITEMS": indicator whether to save the sent mail into the "Send Items" folder of the mail account
* - "MAIL_RECIPIENT": e-mail address of the mail recipient
* - "MAIL_CC_RECIPIENT": optional e-mail address of the cc recipient
* - "MAIL_SUBJECT": subject of the mail
* - "MAIL_ATTACHMENT": optional property containing the base 64 encoded mail attachment
* - "MAIL_ATTACHMENT_CONTENT_TYPE": content type of the mail attachment (example: "text/plain"), only necessary if MAIL_ATTACHMENT is provided
* - "MAIL_ATTACHMENT_NAME": name of the mail attachment, only necessary if MAIL_ATTACHMENT is provided
*/
/* Refer the link below to learn more about the use cases of script.
https://help.sap.com/viewer/368c481cd6954bdfa5d0435479fd4eaf/Cloud/en-US/148851bf8192412cba1f9d2c17f4bd25.html
If you want to know more about the SCRIPT APIs, refer the link below
https://help.sap.com/doc/a56f52e1a58e4e2bac7f7adbf45b2e26/Cloud/en-US/index.html */
import com.sap.gateway.ip.core.customdev.util.Message
import java.util.HashMap
import com.sap.gateway.ip.core.customdev.util.Message
import com.sap.it.api.securestore.SecureStoreService
import com.sap.it.api.securestore.AccessTokenAndUser
import com.sap.it.api.securestore.exception.SecureStoreException
import com.sap.it.api.ITApiFactory
import java.nio.charset.StandardCharsets
import groovy.json.JsonOutput
import java.util.Base64
import java.util.LinkedHashMap
def Message processData(Message message) {
def properties = message.getProperties();
SecureStoreService secureStoreService = ITApiFactory.getService(SecureStoreService.class, null);
// fetch the name of the OAuth2 Authorization Code credential from the property "OAUTH2_AUTHORIZATION_CODE_CRED"
String oauth2AuthorizationCodeCred = properties.get("OAUTH2_AUTHORIZATION_CODE_CRED");
if (oauth2AuthorizationCodeCred == null || oauth2AuthorizationCodeCred.isEmpty()){
throw new IllegalStateException("Property OAUTH2_AUTHORIZATION_CODE_CRED is not specified")
}
// fetch access token
AccessTokenAndUser accessTokenAndUser = secureStoreService.getAccesTokenForOauth2AuthorizationCodeCredential(oauth2AuthorizationCodeCred);
String token = accessTokenAndUser.getAccessToken();
// set the Authorization header with the access token
message.setHeader("Authorization", "Bearer "+token);
message.setHeader("Content-Type","application/json");
MailMessage mailMessage = new MailMessage()
String mailSubject = properties.get("MAIL_SUBJECT");
if (mailSubject == null || mailSubject.isEmpty()){
throw new IllegalStateException("Property MAIL_SUBJECT is not specified")
}
mailMessage.subject = mailSubject
// We assume here that the mail content is provided in the message body as byte array with UTF8 encoding
// and that the content type is provided in the property MAIL_BODY_CONTENT_TYPE
// Possible values vor the content type are "text" and "html".
String contentType = properties.get("MAIL_CONTENT_TYPE");
if (contentType == null || contentType.isEmpty()){
throw new IllegalStateException("Property MAIL_CONTENT_TYPE is not specified")
}
byte[] messageBody = message.getBody(byte[].class)
if (messageBody == null){
throw new IllegalStateException("Message body is null")
}
MailBody mailBody = new MailBody()
mailBody.contentType = contentType
mailBody.content = new String(messageBody,StandardCharsets.UTF_8) // we assume here that the message body is encoded in UTF8
mailMessage.body = mailBody
String mailRecipient = properties.get("MAIL_RECIPIENT");
if (mailRecipient == null || mailRecipient.isEmpty()){
throw new IllegalStateException("Property MAIL_RECIPIENT is not specified")
}
EmailAddress emailAddress = new EmailAddress()
emailAddress.address = mailRecipient
MailRecipient recipient = new MailRecipient()
recipient.emailAddress = emailAddress
// you can also add more than one recipient
mailMessage.toRecipients.add(recipient)
//mailCcRecipient is optional
String mailCcRecipient = properties.get("MAIL_CC_RECIPIENT");
if (mailCcRecipient != null && (! mailCcRecipient.isEmpty())){
EmailAddress ccEmailAddress = new EmailAddress()
ccEmailAddress.address = mailCcRecipient
MailRecipient ccRecipient = new MailRecipient()
ccRecipient.emailAddress = ccEmailAddress
mailMessage.ccRecipients.add(ccRecipient)
}
// attachment is optional, attachment must be base 64 encoded
String attachment = properties.get("MAIL_ATTACHMENT");
if (attachment != null && (! attachment.isEmpty())){
String attachmentContentType = properties.get("MAIL_ATTACHMENT_CONTENT_TYPE");
if (attachmentContentType == null || attachmentContentType.isEmpty()){
throw new IllegalStateException("Property MAIL_ATTACHMENT_CONTENT_TYPE is not specified")
}
String attachmentName = properties.get("MAIL_ATTACHMENT_NAME");
if (attachmentName == null || attachmentName.isEmpty()){
throw new IllegalStateException("Property MAIL_ATTACHMENT_NAME is not specified")
}
Map<String,String> mailAttachment = new LinkedHashMap<>()
mailAttachment.put("@odata.type","#microsoft.graph.fileAttachment")
mailAttachment.put("name", attachmentName)
mailAttachment.put("contentType", attachmentContentType)
mailAttachment.put("contentBytes", attachment )
// you can also add more than one attachment
mailMessage.attachments.add(mailAttachment)
}
Mail mail = new Mail()
mail.message = mailMessage;
String saveToSentItems = properties.get("MAIL_SAVE_TO_SENT_ITEMS");
if (saveToSentItems == null || saveToSentItems.isEmpty()){
// default value is true
mail.saveToSentItems = true
} else {
boolean saveToSentItemsBoolean = Boolean.valueOf(saveToSentItems);
mail.saveToSentItems = saveToSentItemsBoolean
}
String jsonBody = JsonOutput.toJson(mail)
message.setBody(jsonBody.getBytes(StandardCharsets.UTF_8));
return message;
}
class MailBody{
String contentType = "Text"
String content
}
class EmailAddress{
String address
}
class MailRecipient{
EmailAddress emailAddress
}
class MailMessage{
String subject
MailBody body
List<MailRecipient> toRecipients = new LinkedList<MailRecipient>()
List<MailRecipient> ccRecipients = new LinkedList<MailRecipient>()
List<Map<String,String>> attachments = new LinkedList<Map<String,String>>()
}
class Mail {
MailMessage message
boolean saveToSentItems }
Groovy Script utiliza las siguientes propiedades:
Groovy Script utiliza el cuerpo del mensaje como entrada para el cuerpo del correo y asume que el cuerpo del mensaje tiene una codificación de conjunto de caracteres de UTF-8.
Groovy Script solo permite un destinatario, un destinatario cc y un archivo adjunto. Si desea utilizar varios destinatarios, varios destinatarios de cc o varios archivos adjuntos, debe mejorar el Script.
Implemente la colección de scripts.
Creamos un flujo de integración con un paso de guión que usa la credencial del código de autorización OAuth2 creado y un adaptador de receptor HTTP que llama a Microsoft Graph API. La siguiente captura de pantalla muestra el flujo de integración que queremos construir:
El paso del modificador de contenido «Configurar correo» contiene las propiedades de intercambio para el paso Groovy Script:
Las propiedades MAIL_ATTACHMENT_…, MAIL_CC_RECIPIENT y MAIL_SAVE_TO_SEND_ITEMS son opcionales. Si MAIL_SAVE_TO_SEND_ITEMS no está configurado, se utiliza la configuración predeterminada «verdadero». Vea el Groovy Script arriba.
En el paso «Preparar correo» de Groovy Script, hacemos referencia al Groovy Script de la colección de scripts que creamos antes.
Primero, crea una referencia a la colección de scripts:
Luego puede hacer referencia al Script de la colección en el paso Groovy Script:
Creamos una “Solicitud de respuesta” con nombre “Llamar a Microsoft Graph” a la que conectamos un adaptador de receptor HTTP.
Ingrese en el campo “Dirección” “https://graph.microsoft.com/v1.0/me/sendMail“,
elija «Autenticación» «Ninguno»,
e ingrese en los encabezados de solicitud «Autorización | Tipo de contenido» (estos encabezados se establecen en Groovy Script):
Llamamos al adaptador del receptor HTTP en un paso de respuesta de solicitud porque queremos limpiar los encabezados después de la llamada a Microsoft Graph. Por lo tanto, agregamos el paso del modificador de contenido «Eliminar encabezados» después del paso «Solicitud-respuesta». Lo que es más importante, eliminamos el encabezado «Autorización» que establece el paso de guión y que contiene el token. Si no eliminamos este encabezado, el token será parte de la respuesta al remitente y el remitente podría acceder a esta información relevante para la seguridad.
Para poder llamar al extremo del flujo de integración, necesita un usuario con el rol ESBMessaging.send asignado. Asumimos aquí que está familiarizado con la configuración de la autenticación básica en su cliente HTTP (por ejemplo, Postman); no explicamos la parte de autenticación del cliente HTTP.
Después de implementar el flujo de integración, encontrará el punto final del flujo de integración en la Vista de operaciones en el mosaico «Administrar contenido de integración»:
En la vista «Administrar contenido de integración», también verá la colección de scripts implementada «SendMail».
Ahora puede realizar una solicitud HTTP POST desde su cliente HTTP a este punto final con un cuerpo de solicitud. El resultado será que se envía un correo al destinatario que especificó en el Modificador de contenido «Configurar correo». El cuerpo del correo será el cuerpo que usó en la solicitud HTTP. El asunto del correo será el asunto que especificó en la propiedad “CORREO_ASUNTO” del Modificador de Contenido “Configurar Correo”.
Calle Eloy Gonzalo, 27
Madrid, Madrid.
Código Postal 28010
Paseo de la Reforma 26
Colonia Juárez, Cuauhtémoc
Ciudad de México 06600
Real Cariari
Autopista General Cañas,
San José, SJ 40104
Av. Jorge Basadre 349
San Isidro
Lima, LIM 15073