diff --git a/cmd/csaf_aggregator/config.go b/cmd/csaf_aggregator/config.go index 387a7f9..555ef74 100644 --- a/cmd/csaf_aggregator/config.go +++ b/cmd/csaf_aggregator/config.go @@ -39,6 +39,8 @@ type provider struct { Rate *float64 `toml:"rate"` Insecure *bool `toml:"insecure"` Categories *[]string `toml:"categories"` + // ServiceDocument incidates if we should create a service.json document. + ServiceDocument *bool `toml:"create_service_document"` } type config struct { @@ -72,11 +74,23 @@ type config struct { // RemoteValidator configures an optional remote validation. RemoteValidatorOptions *csaf.RemoteValidatorOptions `toml:"remote_validator"` + // ServiceDocument incidates if we should create a service.json document. + ServiceDocument bool `toml:"create_service_document"` + keyMu sync.Mutex key *crypto.Key keyErr error } +// serviceDocument tells if we should generate a service document for a +// given provider. +func (p *provider) serviceDocument(c *config) bool { + if p.ServiceDocument != nil { + return *p.ServiceDocument + } + return c.ServiceDocument +} + // runAsMirror determines if the aggregator should run in mirror mode. func (c *config) runAsMirror() bool { return c.Aggregator.Category != nil && diff --git a/cmd/csaf_aggregator/indices.go b/cmd/csaf_aggregator/indices.go index 40c2a01..daac073 100644 --- a/cmd/csaf_aggregator/indices.go +++ b/cmd/csaf_aggregator/indices.go @@ -185,14 +185,24 @@ func (w *worker) writeROLIE(label string, summaries []summary) error { } } + links := []csaf.Link{{ + Rel: "self", + HRef: feedURL, + }} + + if w.provider.serviceDocument(w.processor.cfg) { + links = append(links, csaf.Link{ + Rel: "service", + HRef: w.processor.cfg.Domain + "/.well-known/csaf-aggregator/" + + w.provider.Name + "/service.json", + }) + } + rolie := &csaf.ROLIEFeed{ Feed: csaf.FeedData{ ID: "csaf-feed-tlp-" + strings.ToLower(label), Title: "CSAF feed (TLP:" + strings.ToUpper(label) + ")", - Link: []csaf.Link{{ - Rel: "self", - HRef: feedURL, - }}, + Link: links, Category: []csaf.ROLIECategory{{ Scheme: "urn:ietf:params:rolie:category:information-type", Term: "csaf", @@ -228,6 +238,56 @@ func (w *worker) writeCategories(label string) error { return util.WriteToFile(path, rcd) } +// writeService writes a service.json document if it is configured. +func (w *worker) writeService() error { + + if !w.provider.serviceDocument(w.processor.cfg) { + return nil + } + labels := make([]string, len(w.summaries)) + var i int + for label := range w.summaries { + labels[i] = strings.ToLower(label) + i++ + } + sort.Strings(labels) + + categories := csaf.ROLIEServiceWorkspaceCollectionCategories{ + Category: []csaf.ROLIEServiceWorkspaceCollectionCategoriesCategory{{ + Scheme: "urn:ietf:params:rolie:category:information-type", + Term: "csaf", + }}, + } + + var collections []csaf.ROLIEServiceWorkspaceCollection + + for _, ts := range labels { + feedName := "csaf-feed-tlp-" + ts + ".json" + + href := w.processor.cfg.Domain + "/.well-known/csaf-aggregator/" + + w.provider.Name + "/" + ts + "/" + feedName + + collection := csaf.ROLIEServiceWorkspaceCollection{ + Title: "CSAF feed (TLP:" + strings.ToUpper(ts) + ")", + HRef: href, + Categories: categories, + } + collections = append(collections, collection) + } + + rsd := &csaf.ROLIEServiceDocument{ + Service: csaf.ROLIEService{ + Workspace: []csaf.ROLIEServiceWorkspace{{ + Title: "CSAF feeds", + Collection: collections, + }}, + }, + } + + path := filepath.Join(w.dir, "service.json") + return util.WriteToFile(path, rsd) +} + func (w *worker) writeIndices() error { if len(w.summaries) == 0 || w.dir == "" { @@ -253,5 +313,5 @@ func (w *worker) writeIndices() error { } } - return nil + return w.writeService() }