55 */
66package org .elasticsearch .xpack .watcher .notification .email .attachment ;
77
8+ import com .google .common .collect .ImmutableMap ;
89import org .apache .logging .log4j .LogManager ;
910import org .apache .logging .log4j .Logger ;
11+ import org .apache .logging .log4j .message .ParameterizedMessage ;
1012import org .elasticsearch .ElasticsearchException ;
1113import org .elasticsearch .common .ParseField ;
1214import org .elasticsearch .common .Strings ;
1315import org .elasticsearch .common .bytes .BytesReference ;
1416import org .elasticsearch .common .logging .LoggerMessageFormat ;
17+ import org .elasticsearch .common .settings .ClusterSettings ;
1518import org .elasticsearch .common .settings .Setting ;
1619import org .elasticsearch .common .settings .Settings ;
1720import org .elasticsearch .common .unit .TimeValue ;
3740import java .io .IOException ;
3841import java .io .InputStream ;
3942import java .io .UncheckedIOException ;
43+ import java .util .ArrayList ;
44+ import java .util .Arrays ;
45+ import java .util .HashSet ;
46+ import java .util .List ;
47+ import java .util .Locale ;
4048import java .util .Map ;
49+ import java .util .Set ;
50+ import java .util .concurrent .ConcurrentHashMap ;
4151
4252public class ReportingAttachmentParser implements EmailAttachmentParser <ReportingAttachment > {
4353
4454 public static final String TYPE = "reporting" ;
4555
4656 // total polling of 10 minutes happens this way by default
47- public static final Setting <TimeValue > INTERVAL_SETTING =
57+ static final Setting <TimeValue > INTERVAL_SETTING =
4858 Setting .timeSetting ("xpack.notification.reporting.interval" , TimeValue .timeValueSeconds (15 ), Setting .Property .NodeScope );
49- public static final Setting <Integer > RETRIES_SETTING =
59+ static final Setting <Integer > RETRIES_SETTING =
5060 Setting .intSetting ("xpack.notification.reporting.retries" , 40 , 0 , Setting .Property .NodeScope );
5161
62+ static final Setting <Boolean > REPORT_WARNING_ENABLED_SETTING =
63+ Setting .boolSetting ("xpack.notification.reporting.warning.enabled" , true , Setting .Property .NodeScope , Setting .Property .Dynamic );
64+
65+ static final Setting .AffixSetting <String > REPORT_WARNING_TEXT =
66+ Setting .affixKeySetting ("xpack.notification.reporting.warning." , "text" ,
67+ key -> Setting .simpleString (key , Setting .Property .NodeScope , Setting .Property .Dynamic ));
68+
5269 private static final ObjectParser <Builder , AuthParseContext > PARSER = new ObjectParser <>("reporting_attachment" );
5370 private static final ObjectParser <KibanaReportingPayload , Void > PAYLOAD_PARSER =
5471 new ObjectParser <>("reporting_attachment_kibana_payload" , true , null );
5572
73+ static final Map <String , String > WARNINGS = ImmutableMap .of ("kbn-csv-contains-formulas" , "Warning: The attachment [%s] contains " +
74+ "characters which spreadsheet applications may interpret as formulas. Please ensure that the attachment is safe prior to opening." );
75+
5676 static {
5777 PARSER .declareInt (Builder ::retries , ReportingAttachment .RETRIES );
5878 PARSER .declareBoolean (Builder ::inline , ReportingAttachment .INLINE );
@@ -63,18 +83,52 @@ public class ReportingAttachmentParser implements EmailAttachmentParser<Reportin
6383 PAYLOAD_PARSER .declareString (KibanaReportingPayload ::setPath , new ParseField ("path" ));
6484 }
6585
86+ private static List <Setting <?>> getDynamicSettings () {
87+ return Arrays .asList (REPORT_WARNING_ENABLED_SETTING , REPORT_WARNING_TEXT );
88+ }
89+
90+ private static List <Setting <?>> getStaticSettings () {
91+ return Arrays .asList (INTERVAL_SETTING , RETRIES_SETTING );
92+ }
93+
94+ public static List <Setting <?>> getSettings () {
95+ List <Setting <?>> allSettings = new ArrayList <Setting <?>>(getDynamicSettings ());
96+ allSettings .addAll (getStaticSettings ());
97+ return allSettings ;
98+ }
6699 private final Logger logger ;
67100 private final TimeValue interval ;
68101 private final int retries ;
69102 private HttpClient httpClient ;
70103 private final TextTemplateEngine templateEngine ;
104+ private boolean warningEnabled = REPORT_WARNING_ENABLED_SETTING .getDefault (Settings .EMPTY );
105+ private final Map <String , String > customWarnings = new ConcurrentHashMap <>(1 );
71106
72- public ReportingAttachmentParser (Settings settings , HttpClient httpClient , TextTemplateEngine templateEngine ) {
107+ public ReportingAttachmentParser (Settings settings , HttpClient httpClient , TextTemplateEngine templateEngine ,
108+ ClusterSettings clusterSettings ) {
73109 this .interval = INTERVAL_SETTING .get (settings );
74110 this .retries = RETRIES_SETTING .get (settings );
75111 this .httpClient = httpClient ;
76112 this .templateEngine = templateEngine ;
77113 this .logger = LogManager .getLogger (getClass ());
114+ clusterSettings .addSettingsUpdateConsumer (REPORT_WARNING_ENABLED_SETTING , this ::setWarningEnabled );
115+ clusterSettings .addAffixUpdateConsumer (REPORT_WARNING_TEXT , this ::addWarningText , this ::warningValidator );
116+ }
117+
118+ void setWarningEnabled (boolean warningEnabled ) {
119+ this .warningEnabled = warningEnabled ;
120+ }
121+
122+ void addWarningText (String name , String value ) {
123+ customWarnings .put (name , value );
124+ }
125+
126+ void warningValidator (String name , String value ) {
127+ if (WARNINGS .keySet ().contains (name ) == false ) {
128+ throw new IllegalArgumentException (new ParameterizedMessage (
129+ "Warning [{}] is not supported. Only the following warnings are supported [{}]" ,
130+ name , String .join (", " , WARNINGS .keySet ())).getFormattedMessage ());
131+ }
78132 }
79133
80134 @ Override
@@ -139,8 +193,24 @@ public Attachment toAttachment(WatchExecutionContext context, Payload payload, R
139193 "method[{}], path[{}], status[{}], body[{}]" , context .watch ().id (), attachment .id (), request .host (),
140194 request .port (), request .method (), request .path (), response .status (), body );
141195 } else if (response .status () == 200 ) {
142- return new Attachment .Bytes (attachment .id (), BytesReference .toBytes (response .body ()),
143- response .contentType (), attachment .inline ());
196+ Set <String > warnings = new HashSet <>(1 );
197+ if (warningEnabled ) {
198+ WARNINGS .forEach ((warningKey , defaultWarning ) -> {
199+ String [] text = response .header (warningKey );
200+ if (text != null && text .length > 0 ) {
201+ if (Boolean .valueOf (text [0 ])) {
202+ String warning = String .format (Locale .ROOT , defaultWarning , attachment .id ());
203+ String customWarning = customWarnings .get (warningKey );
204+ if (Strings .isNullOrEmpty (customWarning ) == false ) {
205+ warning = String .format (Locale .ROOT , customWarning , attachment .id ());
206+ }
207+ warnings .add (warning );
208+ }
209+ }
210+ });
211+ }
212+ return new Attachment .Bytes (attachment .id (), attachment .id (), BytesReference .toBytes (response .body ()),
213+ response .contentType (), attachment .inline (), warnings );
144214 } else {
145215 String body = response .body () != null ? response .body ().utf8ToString () : null ;
146216 String message = LoggerMessageFormat .format ("" , "Watch[{}] reporting[{}] Unexpected status code host[{}], port[{}], " +
0 commit comments