@@ -3,14 +3,18 @@ package models
3
3
import java .util .Date
4
4
import javax .inject .Inject
5
5
6
+ import akka .NotUsed
6
7
import akka .stream .Materializer
8
+ import akka .stream .scaladsl .Source
7
9
import org .elastic4play .models .BaseModelDef
8
10
import org .elastic4play .services ._
11
+ import org .elastic4play .services .JsonFormat .attachmentFormat
9
12
import org .elastic4play .utils
10
13
import org .elastic4play .utils .{ Hasher , RichJson }
11
- import play .api .{ Configuration , Logger }
12
14
import play .api .libs .json .JsValue .jsValueToJsLookup
13
15
import play .api .libs .json ._
16
+ import play .api .{ Configuration , Logger }
17
+ import services .AlertSrv
14
18
15
19
import scala .collection .immutable .{ Set ⇒ ISet }
16
20
import scala .concurrent .{ ExecutionContext , Future }
@@ -21,6 +25,9 @@ case class UpdateMispAlertArtifact() extends EventMessage
21
25
22
26
class Migration (
23
27
mispCaseTemplate : Option [String ],
28
+ mainHash : String ,
29
+ extraHashes : Seq [String ],
30
+ datastoreName : String ,
24
31
models : ISet [BaseModelDef ],
25
32
dblists : DBLists ,
26
33
eventSrv : EventSrv ,
@@ -33,10 +40,17 @@ class Migration(
33
40
eventSrv : EventSrv ,
34
41
ec : ExecutionContext ,
35
42
materializer : Materializer ) = {
36
- this (configuration.getString(" misp.caseTemplate" ), models, dblists, eventSrv, ec, materializer)
43
+ this (
44
+ configuration.getString(" misp.caseTemplate" ),
45
+ configuration.getString(" datastore.hash.main" ).get,
46
+ configuration.getStringSeq(" datastore.hash.extra" ).get,
47
+ configuration.getString(" datastore.name" ).get,
48
+ models, dblists,
49
+ eventSrv, ec, materializer)
37
50
}
38
51
39
52
import org .elastic4play .services .Operation ._
53
+
40
54
val logger = Logger (getClass)
41
55
private var requireUpdateMispAlertArtifact = false
42
56
@@ -126,10 +140,65 @@ class Migration(
126
140
" follow" → (misp \ " follow" ).as[JsBoolean ])
127
141
},
128
142
removeEntity(" audit" )(o ⇒ (o \ " objectType" ).asOpt[String ].contains(" alert" )))
129
- case DatabaseState (9 ) ⇒ Nil
143
+ case ds @ DatabaseState (9 ) ⇒
144
+ object Base64 {
145
+ def unapply (data : String ): Option [Array [Byte ]] = Try (java.util.Base64 .getDecoder.decode(data)).toOption
146
+ }
147
+
148
+ var dataIds = Set .empty[String ]
149
+ def containsOrAdd (id : String ) = {
150
+ dataIds.synchronized {
151
+ if (dataIds.contains(id)) true
152
+ else {
153
+ dataIds = dataIds + id
154
+ false
155
+ }
156
+ }
157
+ }
158
+ val mainHasher = Hasher (mainHash)
159
+ val extraHashers = Hasher (mainHash +: extraHashes : _* )
160
+ Seq (
161
+ Operation ((f : String ⇒ Source [JsObject , NotUsed ]) ⇒ {
162
+ case " alert" ⇒ f(" alert" ).flatMapConcat { alert ⇒
163
+ val artifactsAndData = Future .traverse((alert \ " artifacts" ).asOpt[List [JsObject ]].getOrElse(Nil )) { artifact ⇒
164
+ (artifact \ " data" ).asOpt[String ]
165
+ .collect {
166
+ case AlertSrv .dataExtractor(filename, contentType, data @ Base64 (rawData)) ⇒
167
+ val attachmentId = mainHasher.fromByteArray(rawData).head.toString()
168
+ ds.getEntity(datastoreName, s " ${attachmentId}_0 " )
169
+ .map(_ ⇒ Nil )
170
+ .recover {
171
+ case _ if containsOrAdd(attachmentId) ⇒ Nil
172
+ case _ ⇒
173
+ Seq (Json .obj(
174
+ " _type" → datastoreName,
175
+ " _id" → s " ${attachmentId}_0 " ,
176
+ " data" → data))
177
+ }
178
+ .map { dataEntity ⇒
179
+ val attachment = Attachment (filename, extraHashers.fromByteArray(rawData), rawData.length.toLong, contentType, attachmentId)
180
+ (artifact - " data" + (" attachment" → Json .toJson(attachment))) → dataEntity
181
+ }
182
+ }
183
+ .getOrElse(Future .successful(artifact → Nil ))
184
+ }
185
+ Source .fromFuture(artifactsAndData)
186
+ .mapConcat { ad ⇒
187
+ val updatedAlert = alert + (" artifacts" → JsArray (ad.map(_._1)))
188
+ updatedAlert :: ad.flatMap(_._2)
189
+ }
190
+ }
191
+ case other ⇒ f(other)
192
+ }),
193
+ mapAttribute(" alert" , " status" ) {
194
+ case JsString (" Update" ) ⇒ JsString (" Updated" )
195
+ case JsString (" Ignore" ) ⇒ JsString (" Ignored" )
196
+ case other ⇒ other
197
+ })
130
198
}
131
199
132
200
private val requestCounter = new java.util.concurrent.atomic.AtomicInteger (0 )
201
+
133
202
def getRequestId : String = {
134
203
utils.Instance .id + " :mig:" + requestCounter.incrementAndGet()
135
204
}
0 commit comments