forked from freetaxii/libstix2
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
t_collectiondataManifest.go
715 lines (621 loc) · 23.6 KB
/
t_collectiondataManifest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
// Copyright 2015-2019 Bret Jordan, All rights reserved.
//
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file in the root of the source tree.
package sqlite3
import (
"bytes"
"errors"
"fmt"
"strconv"
"github.com/freetaxii/libstix2/defs"
"github.com/freetaxii/libstix2/resources/collections"
"github.com/freetaxii/libstix2/resources/manifest"
"github.com/freetaxii/libstix2/stixid"
"github.com/freetaxii/libstix2/timestamp"
)
// ----------------------------------------------------------------------
//
// Collection Data Table Private Functions and Methods
// getManifestData
//
// ----------------------------------------------------------------------
/*
getManifestData - This method will return manifest data based on the query provided.
The SQL statement that is built in this method will return a list of objects
from a given collection and all of the information needed to create the manifest
resource. It will use the query struct to determine the requirements and
parameters for the where clause of the SQL statement. A byte array is used
instead of sting concatenation as it is the most efficient way to do string
concatenation in Go.
*/
func (ds *Store) getManifestData(query collections.CollectionQuery) (*collections.CollectionQueryResult, error) {
ds.Logger.Levelln("Function", "FUNC: getManifestData start")
// Lets first make sure the collection exists in the cache
if found := ds.doesCollectionExistInTheCache(query.CollectionUUID); !found {
ds.Logger.Levelln("Function", "FUNC: getManifestData exited with an error")
return nil, fmt.Errorf("the following collection id was not found in the cache", query.CollectionUUID)
}
query.CollectionDatastoreID = ds.Cache.Collections[query.CollectionUUID].DatastoreID
var resultData collections.CollectionQueryResult
manifestData := manifest.New()
// Create SQL Statement
/*
SELECT
t_collection_data.stix_id,
s_base_object.date_added,
s_base_object.modified,
s_base_object.spec_version
FROM
t_collection_data
JOIN
s_base_object ON
t_collection_data.stix_id = s_base_object.id
WHERE
t_collection_data.collection_id = ?
ORDER BY
s_base_object.date_added
LIMIT 5
*/
// If an error is found, that means a query parameter was passed incorrectly
// and we should return an error versus just skipping the option.
whereQuery, err := ds.sqlCollectionDataQueryOptions(query)
if err != nil {
ds.Logger.Levelln("Function", "FUNC: getManifestData exited with an error,", err)
return nil, err
}
// If the client passes in an invalid value, then the server limit is used
limitQuery := ds.sqlQueryLimit(query)
tblColData := DB_TABLE_TAXII_COLLECTION_DATA
tblBaseObj := DB_TABLE_STIX_BASE_OBJECT
var sqlstmt bytes.Buffer
sqlstmt.WriteString("SELECT ")
sqlstmt.WriteString(tblColData)
sqlstmt.WriteString(".stix_id, ")
sqlstmt.WriteString(tblBaseObj)
sqlstmt.WriteString(".date_added, ")
sqlstmt.WriteString(tblBaseObj)
sqlstmt.WriteString(".modified, ")
sqlstmt.WriteString(tblBaseObj)
sqlstmt.WriteString(".spec_version ")
sqlstmt.WriteString("FROM ")
sqlstmt.WriteString(tblColData)
sqlstmt.WriteString(" JOIN ")
sqlstmt.WriteString(tblBaseObj)
sqlstmt.WriteString(" ON ")
sqlstmt.WriteString(tblColData)
sqlstmt.WriteString(".stix_id = ")
sqlstmt.WriteString(tblBaseObj)
sqlstmt.WriteString(".id ")
sqlstmt.WriteString("WHERE ")
sqlstmt.WriteString(whereQuery)
sqlstmt.WriteString(" ORDER BY ")
sqlstmt.WriteString(tblBaseObj)
sqlstmt.WriteString(".date_added ASC ")
if limitQuery != 0 {
sqlstmt.WriteString(" LIMIT ")
// We need to ask for limit + 1 to see if there are more records available
// but only if a limit is being enforced. Later on we will check the number
// of records that comes back to see if we got limit or limit+1
limitQueryPagination := limitQuery + 1
i := strconv.Itoa(limitQueryPagination)
sqlstmt.WriteString(i)
}
stmt := sqlstmt.String()
// Make SQL Call
// Query database for all the collection entries
rows, err := ds.DB.Query(stmt)
if err != nil {
ds.Logger.Levelln("Function", "FUNC: getManifestData exited with an error,", err)
return nil, fmt.Errorf("database execution error getting collection data: ", err)
}
defer rows.Close()
// Loop through all records returned and build a manifest resource
counter := 0
for rows.Next() {
var stixid, dateAdded, modified, specVersion string
if err := rows.Scan(&stixid, &dateAdded, &modified, &specVersion); err != nil {
rows.Close()
ds.Logger.Levelln("Function", "FUNC: getManifestData exited with an error,", err)
return nil, fmt.Errorf("database scan error getting collection data: ", err)
}
if counter == limitQuery {
// If we have added the number of records that the limit query is
// set to, then lets not add the last record, but rather set the
// pagination value of more to true and eject from the for loop
manifestData.SetMore()
continue
}
switch specVersion {
case "2.0":
specVersion = defs.MEDIA_TYPE_STIX20
case "2.1":
specVersion = defs.MEDIA_TYPE_STIX21
case "2.2":
specVersion = defs.MEDIA_TYPE_STIX22
case "2.3":
specVersion = defs.MEDIA_TYPE_STIX23
case "2.4":
specVersion = defs.MEDIA_TYPE_STIX24
default:
specVersion = defs.MEDIA_TYPE_STIX
}
manifestData.CreateRecord(stixid, dateAdded, modified, specVersion)
counter++
}
// Errors can cause the rows.Next() to exit prematurely, if this happens lets
// check for the error and handle it.
if err := rows.Err(); err != nil {
rows.Close()
ds.Logger.Levelln("Function", "FUNC: getManifestData exited with an error,", err)
return nil, fmt.Errorf("database rows error getting manifest data: ", err)
}
if len(manifestData.Objects) == 0 {
ds.Logger.Levelln("Function", "FUNC: getManifestData exited with an error")
return nil, fmt.Errorf("no records returned getting manifest data")
}
resultData.Size = ds.Cache.Collections[query.CollectionUUID].Size
ds.Logger.Debugln("DEBUG: Query Collection ID", query.CollectionUUID)
ds.Logger.Debugln("DEBUG: Cache ID", ds.Cache.Collections[query.CollectionUUID].ID, "Cache Datastore ID", ds.Cache.Collections[query.CollectionUUID].DatastoreID, "Size in Cache", ds.Cache.Collections[query.CollectionUUID].Size)
resultData.ManifestData.More = manifestData.More
resultData.ManifestData.Objects = manifestData.Objects
// ----------------------------------------------------------------------
// This is the old pagination code
// first, last, errRange = ds.processRangeValues(query.RangeBegin, query.RangeEnd, query.ServerRecordLimit, resultData.Size)
// if errRange != nil {
// return nil, errRange
// }
// Get a new slice based on the range of records
// resultData.ManifestData.Objects = manifest.Objects[first:last]
// ----------------------------------------------------------------------
resultData.DateAddedFirst = resultData.ManifestData.Objects[0].DateAdded
resultData.DateAddedLast = resultData.ManifestData.Objects[len(resultData.ManifestData.Objects)-1].DateAdded
ds.Logger.Levelln("Function", "FUNC: getManifestData end")
return &resultData, nil
}
// ----------------------------------------------------------------------
//
// LIMIT statements for Collection Data Queries
//
// ----------------------------------------------------------------------
/*
sqlQueryLimit - This method will take in a query struct and build an SQL LIMIT
statement based on the values provided in the query object.
*/
func (ds *Store) sqlQueryLimit(query collections.CollectionQuery) int {
srv := 0
client := 0
var err error
// In the configuration, if the server record limit is 0 or a negative number
// than the value is zero and thus no limit will be applied.
if query.ServerRecordLimit > 0 {
srv = query.ServerRecordLimit
}
// Lets check to see if the client passed in a valid value over the URL
if query.Limit != nil {
client, err = strconv.Atoi(query.Limit[0])
}
if err != nil {
ds.Logger.Debugln("DEBUG: Client limit value is not valid: ", err)
return srv
}
if client > srv {
ds.Logger.Debugln("DEBUG: Client limit value is greater than the server limit, using server limit of", srv)
return srv
} else if client < 0 {
ds.Logger.Debugln("DEBUG: Client limit value is less than zero, using server limit of", srv)
return srv
} else if client == 0 {
ds.Logger.Debugln("DEBUG: Client limit value is equal to zero, using server limit of", srv)
return srv
} else if client == srv {
ds.Logger.Debugln("DEBUG: Client limit value is equal to server limit, using server limit of", srv)
return srv
} else if client < srv {
ds.Logger.Debugln("DEBUG: Client limit value is less than server limit, using client limit of", client)
return client
}
return 0
}
// ----------------------------------------------------------------------
//
// WHERE statements for Collection Data Queries
//
// ----------------------------------------------------------------------
/*
sqlCollectionDataQueryOptions - This method will take in a query struct and
build an SQL where statement based on all of the provided query parameters.
*/
func (ds *Store) sqlCollectionDataQueryOptions(query collections.CollectionQuery) (string, error) {
var wherestmt bytes.Buffer
var err error
// ----------------------------------------------------------------------
// Lets first add the collection ID to the where clause.
// ----------------------------------------------------------------------
if err = sqlCollectionDataWhereCollectionID(query.CollectionDatastoreID, &wherestmt); err != nil {
return "", err
}
// ----------------------------------------------------------------------
// Check to see if an added after query was supplied. There can only be one
// added after option, it does not make sense to have multiple.
// ----------------------------------------------------------------------
if err = sqlCollectionDataWhereAddedAfter(query.AddedAfter, &wherestmt); err != nil {
return "", err
}
// ----------------------------------------------------------------------
// Check to see if one or more STIX IDs was supplied.
// If there is more than one option given we need to enclose the options in
// parentheses as the comma represents an OR operator.
// ----------------------------------------------------------------------
if err = sqlCollectionDataWhereSTIXID(query.STIXID, &wherestmt); err != nil {
return "", err
}
// ----------------------------------------------------------------------
// Check to see if one or more STIX types, to query on, was supplied.
// If there is more than one option given we need to enclose the options in
// parentheses as the comma represents an OR operator.
// ----------------------------------------------------------------------
if err = sqlCollectionDataWhereSTIXType(query.STIXType, &wherestmt); err != nil {
return "", err
}
// ----------------------------------------------------------------------
// Check to see if one or more STIX versions to query on was supplied.
// If there is more than one option given, we need to enclose the options in
// parentheses as the comma represents an OR operator.
// ----------------------------------------------------------------------
if err = sqlCollectionDataWhereSTIXVersion(query.STIXVersion, &wherestmt); err != nil {
return "", err
}
// ----------------------------------------------------------------------
// Check to see if one or more sepc versions to query on was supplied.
// If there is more than one option given, we need to enclose the options in
// parentheses as the comma represents an OR operator.
// ----------------------------------------------------------------------
if err = sqlCollectionDataWhereSpecVersion(query.SpecVersion, &wherestmt); err != nil {
return "", err
}
return wherestmt.String(), nil
}
/*
sqlCollectionDataWhereCollectionID - This function will build the correct WHERE
statement for a provided collection ID (datastore id) value and is called from
func sqlCollectionDataQueryOptions(query collections.CollectionQueryType) (string, error)
*/
func sqlCollectionDataWhereCollectionID(datastoreID int, b *bytes.Buffer) error {
tblColData := DB_TABLE_TAXII_COLLECTION_DATA
/*
This sql where statement should look like:
t_collection_data.collection_id = "some collection id"
*/
if datastoreID != 0 {
b.WriteString(tblColData)
b.WriteString(`.collection_id = `)
// Even though we are matching an integer in the database we need to
// write a string out since the sql statement itself is a string.
b.WriteString(strconv.Itoa(datastoreID))
} else {
return errors.New("no collection ID was provided")
}
return nil
}
/*
sqlCollectionDataWhereAddedAfter - This function will build the correct WHERE
statement for a provided added after value and is called from
func sqlCollectionDataQueryOptions(query collections.CollectionQueryType) (string, error)
This method only supports a single added after value, since more than one does
not make sense.
*/
func sqlCollectionDataWhereAddedAfter(date []string, b *bytes.Buffer) error {
tblBaseObj := DB_TABLE_STIX_BASE_OBJECT
/*
This sql where statement should look like:
t_collection_data.collection_id = "aa" AND
t_collection_data.date_added > "2017"
*/
if date != nil {
// We are only allowing a single added after value, since having more does
// not make sense.
if timestamp.Valid(date[0]) {
b.WriteString(" AND ")
b.WriteString(tblBaseObj)
b.WriteString(`.date_added > "`)
b.WriteString(date[0])
b.WriteString(`"`)
} else {
return errors.New("the provided timestamp for added after is invalid")
}
}
return nil
}
/*
sqlCollectionDataWhereSTIXID - This function will build the correct WHERE
statement when one or more STIX IDs is provided and is called from
func sqlCollectionDataQueryOptions(query collections.CollectionQueryType) (string, error)
*/
func sqlCollectionDataWhereSTIXID(id []string, b *bytes.Buffer) error {
tblColData := DB_TABLE_TAXII_COLLECTION_DATA
/*
This sql where statement should look like one of these two:
t_collection_data.collection_id = "aa" AND
t_collection_data.stix_id = "indicator--37abef16-7616-439c-86be-23712030c4b7"
t_collection_data.collection_id = "aa" AND
(t_collection_data.stix_id = "indicator--37abef16-7616-439c-86be-23712030c4b7" OR
t_collection_data.stix_id = "attack-pattern--c7c8a099-70a9-487b-a95f-2498d2941104" OR
t_collection_data.stix_id = "campaign--6f938db5-6648-4ec1-81cb-5b65138c3c66")
*/
if id != nil {
if len(id) == 1 {
if stixid.ValidSTIXID(id[0]) {
b.WriteString(" AND ")
b.WriteString(tblColData)
b.WriteString(`.stix_id = "`)
b.WriteString(id[0])
b.WriteString(`"`)
} else {
return errors.New("invalid SQL where statement, the provided object id is invalid")
}
} else if len(id) > 1 {
b.WriteString(" AND (")
addOR := false
for _, v := range id {
// Lets only add the OR after the first object id and not after the last object id
if addOR == true {
b.WriteString(" OR ")
addOR = false
}
// Lets make sure the value that was passed in is actually a valid id
if stixid.ValidSTIXID(v) {
b.WriteString(tblColData)
b.WriteString(`.stix_id = "`)
b.WriteString(v)
b.WriteString(`"`)
addOR = true
} else {
return errors.New("invalid SQL where statement, the provided object id is invalid")
}
}
b.WriteString(")")
}
}
return nil
}
/*
sqlCollectionDataWhereSTIXType - This function will build the correct WHERE
statement when one or more STIX types is provided and is called from
func sqlCollectionDataQueryOptions(query collections.CollectionQueryType) (string, error)
*/
func sqlCollectionDataWhereSTIXType(t []string, b *bytes.Buffer) error {
tblColData := DB_TABLE_TAXII_COLLECTION_DATA
/*
This sql where statement should look like one of these two:
t_collection_data.collection_id = "aa" AND
t_collection_data.stix_id LIKE "indicator%"
t_collection_data.collection_id = "aa" AND
(t_collection_data.stix_id LIKE "indicator%" OR
t_collection_data.stix_id LIKE "attack-pattern%" OR
t_collection_data.stix_id LIKE "campaign%")
*/
if t != nil {
if len(t) == 1 {
if stixid.ValidSTIXObjectType(t[0]) {
b.WriteString(" AND ")
b.WriteString(tblColData)
b.WriteString(`.stix_id LIKE "`)
b.WriteString(t[0])
b.WriteString(`%"`)
} else {
return errors.New("the provided object type is invalid")
}
} else if len(t) > 1 {
b.WriteString(" AND (")
addOR := false
for _, v := range t {
// Lets only add the OR after the first object and not after the last object
if addOR == true {
b.WriteString(" OR ")
addOR = false
}
// Lets make sure the value that was passed in is actually a valid object
if stixid.ValidSTIXObjectType(v) {
b.WriteString(tblColData)
b.WriteString(`.stix_id LIKE "`)
b.WriteString(v)
b.WriteString(`%"`)
addOR = true
} else {
return errors.New("the provided object type is invalid")
}
}
b.WriteString(`)`)
}
}
return nil
}
/*
sqlCollectionDataWhereSTIXVersion - This function will build the correct WHERE
statement when one or more STIX versions is provided and is called from
func sqlCollectionDataQueryOptions(query collections.CollectionQueryType) (string, error).
It will return an error if multiple "all", "first", or "last" values is provided.
*/
func sqlCollectionDataWhereSTIXVersion(vers []string, b *bytes.Buffer) error {
tblColData := DB_TABLE_TAXII_COLLECTION_DATA
tblBaseObj := DB_TABLE_STIX_BASE_OBJECT
// If no version parameter was supplied, then set "last" as the default
if vers == nil {
vers = append(vers, "last")
}
// Lets check the multiple version use case and see if the options are valid
if len(vers) > 1 {
counter := make(map[string]int)
for _, v := range vers {
counter[v]++
if counter[v] > 1 {
return errors.New("can not use the same URL parameter more than once in the version selector")
}
}
// This check ensures that it is only used in a single URL parameter use case.
if counter["all"] > 0 {
return errors.New("can not use the 'all' key word with a multiple version selector")
}
}
/*
This sql where statement should look like one of the following:
t_collection_data.collection_id = "aa" AND
s_base_object.modified = "2017-12-05T02:43:19.783Z"
t_collection_data.collection_id = "aa" AND
s_base_object.modified = (select max(modified) from s_base_object where t_collection_data.stix_id = s_base_object.id)
t_collection_data.collection_id = "aa" AND
s_base_object.modified = (select min(modified) from s_base_object where t_collection_data.stix_id = s_base_object.id)
t_collection_data.collection_id = "aa" AND
(s_base_object.modified = (select min(modified) from s_base_object where t_collection_data.stix_id = s_base_object.id) OR
s_base_object.modified = (select max(modified) from s_base_object where t_collection_data.stix_id = s_base_object.id))
t_collection_data.collection_id = "aa" AND
(s_base_object.modified = "2017-12-05T02:43:19.783Z" OR
s_base_object.modified = "2017-12-05T02:43:23.822Z" OR
s_base_object.modified = "2017-12-05T02:43:24.835Z")
*/
if len(vers) == 1 {
if vers[0] == "last" {
// s_base_object.modified = (select max(modified) from s_base_object where t_collection_data.stix_id = s_base_object.id)
b.WriteString(" AND ")
b.WriteString(tblBaseObj)
b.WriteString(`.modified = (select max(modified) from `)
b.WriteString(tblBaseObj)
b.WriteString(` where `)
b.WriteString(tblColData)
b.WriteString(`.stix_id = `)
b.WriteString(tblBaseObj)
b.WriteString(`.id)`)
} else if vers[0] == "first" {
// s_base_object.modified = (select min(modified) from s_base_object where t_collection_data.stix_id = s_base_object.id)
b.WriteString(" AND ")
b.WriteString(tblBaseObj)
b.WriteString(`.modified = (select min(modified) from `)
b.WriteString(tblBaseObj)
b.WriteString(` where `)
b.WriteString(tblColData)
b.WriteString(`.stix_id = `)
b.WriteString(tblBaseObj)
b.WriteString(`.id)`)
} else if vers[0] == "all" {
// Do nothing, since the default is to return all versions.
} else {
if timestamp.Valid(vers[0]) {
b.WriteString(" AND ")
b.WriteString(tblBaseObj)
b.WriteString(`.modified = "`)
b.WriteString(vers[0])
b.WriteString(`"`)
} else {
return errors.New("the provided timestamp for the version is invalid")
}
}
} else if len(vers) > 1 {
b.WriteString(" AND (")
for i, v := range vers {
// Lets only add he OR after the first object and not after the
// last object. Since skipOr starts as true, this takes care of
// the first run case where i == 0
if i > 0 {
b.WriteString(" OR ")
}
if v == "last" {
b.WriteString(tblBaseObj)
b.WriteString(`.modified = (select max(modified) from `)
b.WriteString(tblBaseObj)
b.WriteString(` where `)
b.WriteString(tblColData)
b.WriteString(`.stix_id = `)
b.WriteString(tblBaseObj)
b.WriteString(`.id)`)
} else if v == "first" {
b.WriteString(tblBaseObj)
b.WriteString(`.modified = (select min(modified) from `)
b.WriteString(tblBaseObj)
b.WriteString(` where `)
b.WriteString(tblColData)
b.WriteString(`.stix_id = `)
b.WriteString(tblBaseObj)
b.WriteString(`.id)`)
} else {
if timestamp.Valid(v) {
b.WriteString(tblBaseObj)
b.WriteString(`.modified = "`)
b.WriteString(v)
b.WriteString(`"`)
} else {
return errors.New("the provided timestamp for the version is invalid")
}
}
}
b.WriteString(`)`)
}
return nil
}
/*
sqlCollectionDataWhereSpecVersion - This function will build the correct WHERE
statement when one or more STIX versions is provided and is called from
func sqlCollectionDataQueryOptions(query collections.CollectionQueryType) (string, error).
*/
func sqlCollectionDataWhereSpecVersion(vers []string, b *bytes.Buffer) error {
tblColData := DB_TABLE_TAXII_COLLECTION_DATA
tblBaseObj := DB_TABLE_STIX_BASE_OBJECT
// If we add another version here, we need to also add it in the FreeTAXII
// Server code in server.go processURLParameters()
/*
This sql where statement should look like one of the following:
t_collection_data.collection_id = "aa" AND
s_base_object.spec_version = "2.0"
t_collection_data.collection_id = "aa" AND
(s_base_object.spec_version = "2.0" OR
s_base_object.spec_version = "2.1")
*/
if len(vers) == 1 {
if vers[0] == "2.0" {
b.WriteString(" AND ")
b.WriteString(tblBaseObj)
b.WriteString(`.spec_version = "2.0" `)
} else if vers[0] == "2.1" {
b.WriteString(" AND ")
b.WriteString(tblBaseObj)
b.WriteString(`.spec_version = "2.1" `)
} else {
// s_base_object.modified = (select max(modified) from s_base_object where t_collection_data.stix_id = s_base_object.id)
b.WriteString(" AND ")
b.WriteString(tblBaseObj)
b.WriteString(`.spec_version = (select max(modified) from `)
b.WriteString(tblBaseObj)
b.WriteString(` where `)
b.WriteString(tblColData)
b.WriteString(`.stix_id = `)
b.WriteString(tblBaseObj)
b.WriteString(`.id)`)
}
} else if len(vers) > 1 {
b.WriteString(" AND (")
counter := make(map[string]int)
for i, v := range vers {
// First lets keep track of each element to make sure that the same
// element is not being used more than once.
counter[v]++
if counter[v] > 1 {
return errors.New("can not use the same URL parameter more than once")
}
// Lets only add he OR after the first object and not after the
// last object. Since skipOr starts as true, this takes care of
// the first run case where i == 0
if i > 0 {
b.WriteString(" OR ")
}
if v == "2.0" {
b.WriteString(tblBaseObj)
b.WriteString(`.spec_version = "2.0"`)
} else if v == "2.1" {
b.WriteString(tblBaseObj)
b.WriteString(`.spec_version = "2.1"`)
}
}
b.WriteString(`)`)
}
return nil
}