1
0
mirror of https://github.com/strongdm/comply synced 2024-11-25 00:54:54 +00:00

the data model for Narratives and Policies have converged, allowing both to be represented by a common Docume

nt struct.
This commit is contained in:
Justin McCarthy 2018-05-28 17:05:56 -05:00
parent ff626a5ee2
commit 815e7e5f61
No known key found for this signature in database
GPG Key ID: 900437410E142A48
8 changed files with 19 additions and 173 deletions

View File

@ -91,8 +91,8 @@ func ReadStandards() ([]*Standard, error) {
} }
// ReadNarratives loads narrative descriptions from the filesystem. // ReadNarratives loads narrative descriptions from the filesystem.
func ReadNarratives() ([]*Narrative, error) { func ReadNarratives() ([]*Document, error) {
var narratives []*Narrative var narratives []*Document
files, err := path.Narratives() files, err := path.Narratives()
if err != nil { if err != nil {
@ -100,7 +100,7 @@ func ReadNarratives() ([]*Narrative, error) {
} }
for _, f := range files { for _, f := range files {
n := &Narrative{} n := &Document{}
mdmd := loadMDMD(f.FullPath) mdmd := loadMDMD(f.FullPath)
err = yaml.Unmarshal([]byte(mdmd.yaml), &n) err = yaml.Unmarshal([]byte(mdmd.yaml), &n)
if err != nil { if err != nil {
@ -141,8 +141,8 @@ func ReadProcedures() ([]*Procedure, error) {
} }
// ReadPolicies loads policy documents from the filesystem. // ReadPolicies loads policy documents from the filesystem.
func ReadPolicies() ([]*Policy, error) { func ReadPolicies() ([]*Document, error) {
var policies []*Policy var policies []*Document
files, err := path.Policies() files, err := path.Policies()
if err != nil { if err != nil {
@ -150,7 +150,7 @@ func ReadPolicies() ([]*Policy, error) {
} }
for _, f := range files { for _, f := range files {
p := &Policy{} p := &Document{}
mdmd := loadMDMD(f.FullPath) mdmd := loadMDMD(f.FullPath)
err = yaml.Unmarshal([]byte(mdmd.yaml), &p) err = yaml.Unmarshal([]byte(mdmd.yaml), &p)
if err != nil { if err != nil {

View File

@ -2,8 +2,8 @@ package model
type Data struct { type Data struct {
Standards []*Standard Standards []*Standard
Narratives []*Narrative Narratives []*Document
Policies []*Policy Policies []*Document
Procedures []*Procedure Procedures []*Procedure
Tickets []*Ticket Tickets []*Ticket
Audits []*Audit Audits []*Audit

View File

@ -1,15 +0,0 @@
package model
import "time"
type Narrative struct {
Name string `yaml:"name"`
Acronym string `yaml:"acronym"`
Revisions []Revision `yaml:"majorRevisions"`
Satisfies Satisfaction `yaml:"satisfies"`
FullPath string
OutputFilename string
ModifiedAt time.Time
Body string
}

View File

@ -1,15 +0,0 @@
package model
import "time"
type Policy struct {
Name string `yaml:"name"`
Acronym string `yaml:"acronym"`
Revisions []Revision `yaml:"majorRevisions"`
Satisfies Satisfaction `yaml:"satisfies"`
FullPath string
OutputFilename string
ModifiedAt time.Time
Body string
}

View File

@ -32,8 +32,8 @@ type renderData struct {
Name string Name string
Project *project Project *project
Stats *stats Stats *stats
Narratives []*model.Narrative Narratives []*model.Document
Policies []*model.Policy Policies []*model.Document
Procedures []*model.Procedure Procedures []*model.Procedure
Standards []*model.Standard Standards []*model.Standard
Tickets []*model.Ticket Tickets []*model.Ticket

View File

@ -17,20 +17,20 @@ import (
) )
// TODO: refactor and eliminate duplication among narrative, policy renderers // TODO: refactor and eliminate duplication among narrative, policy renderers
func renderPolicyToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, policy *model.Policy, live bool) { func renderToFilesystem(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, doc *model.Document, live bool) {
// only files that have been touched // only files that have been touched
if !isNewer(policy.FullPath, policy.ModifiedAt) { if !isNewer(doc.FullPath, doc.ModifiedAt) {
return return
} }
recordModified(policy.FullPath, policy.ModifiedAt) recordModified(doc.FullPath, doc.ModifiedAt)
wg.Add(1) wg.Add(1)
go func(p *model.Policy) { go func(p *model.Document) {
defer wg.Done() defer wg.Done()
outputFilename := p.OutputFilename outputFilename := p.OutputFilename
// save preprocessed markdown // save preprocessed markdown
err := preprocessPolicy(data, p, filepath.Join(".", "output", outputFilename+".md")) err := preprocessDoc(data, p, filepath.Join(".", "output", outputFilename+".md"))
if err != nil { if err != nil {
errOutputCh <- errors.Wrap(err, "unable to preprocess") errOutputCh <- errors.Wrap(err, "unable to preprocess")
return return
@ -50,10 +50,10 @@ func renderPolicyToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *render
rel = p.FullPath rel = p.FullPath
} }
fmt.Printf("%s -> %s\n", rel, filepath.Join("output", p.OutputFilename)) fmt.Printf("%s -> %s\n", rel, filepath.Join("output", p.OutputFilename))
}(policy) }(doc)
} }
func preprocessPolicy(data *renderData, pol *model.Policy, fullPath string) error { func preprocessDoc(data *renderData, pol *model.Document, fullPath string) error {
cfg := config.Config() cfg := config.Config()
var w bytes.Buffer var w bytes.Buffer

View File

@ -1,124 +0,0 @@
package render
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"text/template"
"time"
"github.com/pkg/errors"
"github.com/strongdm/comply/internal/config"
"github.com/strongdm/comply/internal/model"
)
// TODO: refactor and eliminate duplication among narrative, policy renderers
func renderNarrativeToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, narrative *model.Narrative, live bool) {
// only files that have been touched
if !isNewer(narrative.FullPath, narrative.ModifiedAt) {
return
}
recordModified(narrative.FullPath, narrative.ModifiedAt)
wg.Add(1)
go func(p *model.Narrative) {
defer wg.Done()
outputFilename := p.OutputFilename
// save preprocessed markdown
err := preprocessNarrative(data, p, filepath.Join(".", "output", outputFilename+".md"))
if err != nil {
errOutputCh <- errors.Wrap(err, "unable to preprocess")
return
}
pandoc(outputFilename, errOutputCh)
// remove preprocessed markdown
err = os.Remove(filepath.Join(".", "output", outputFilename+".md"))
if err != nil {
errOutputCh <- err
return
}
rel, err := filepath.Rel(config.ProjectRoot(), p.FullPath)
if err != nil {
rel = p.FullPath
}
fmt.Printf("%s -> %s\n", rel, filepath.Join("output", p.OutputFilename))
}(narrative)
}
func preprocessNarrative(data *renderData, pol *model.Narrative, fullPath string) error {
cfg := config.Config()
var w bytes.Buffer
bodyTemplate, err := template.New("body").Parse(pol.Body)
if err != nil {
w.WriteString(fmt.Sprintf("# Error processing template:\n\n%s\n", err.Error()))
} else {
bodyTemplate.Execute(&w, data)
}
body := w.String()
revisionTable := ""
satisfiesTable := ""
// ||Date|Comment|
// |---+------|
// | 4 Jan 2018 | Initial Version |
// Table: Document history
if len(pol.Satisfies) > 0 {
rows := ""
for standard, keys := range pol.Satisfies {
rows += fmt.Sprintf("| %s | %s |\n", standard, strings.Join(keys, ", "))
}
satisfiesTable = fmt.Sprintf("|Standard|Controls Satisfied|\n|-------+--------------------------------------------|\n%s\nTable: Control satisfaction\n", rows)
}
if len(pol.Revisions) > 0 {
rows := ""
for _, rev := range pol.Revisions {
rows += fmt.Sprintf("| %s | %s |\n", rev.Date, rev.Comment)
}
revisionTable = fmt.Sprintf("|Date|Comment|\n|---+--------------------------------------------|\n%s\nTable: Document history\n", rows)
}
doc := fmt.Sprintf(`%% %s
%% %s
%% %s
---
header-includes: yes
head-content: "%s"
foot-content: "%s confidential %d"
---
%s
%s
\newpage
%s`,
pol.Name,
cfg.Name,
fmt.Sprintf("%s %d", pol.ModifiedAt.Month().String(), pol.ModifiedAt.Year()),
pol.Name,
cfg.Name,
time.Now().Year(),
satisfiesTable,
revisionTable,
body,
)
err = ioutil.WriteFile(fullPath, []byte(doc), os.FileMode(0644))
if err != nil {
return errors.Wrap(err, "unable to write preprocessed narrative to disk")
}
return nil
}

View File

@ -25,7 +25,7 @@ func pdf(output string, live bool, errCh chan error, wg *sync.WaitGroup) {
return return
} }
for _, policy := range policies { for _, policy := range policies {
renderPolicyToDisk(&pdfWG, errOutputCh, data, policy, live) renderToFilesystem(&pdfWG, errOutputCh, data, policy, live)
} }
narratives, err := model.ReadNarratives() narratives, err := model.ReadNarratives()
@ -35,7 +35,7 @@ func pdf(output string, live bool, errCh chan error, wg *sync.WaitGroup) {
} }
for _, narrative := range narratives { for _, narrative := range narratives {
renderNarrativeToDisk(&pdfWG, errOutputCh, data, narrative, live) renderToFilesystem(&pdfWG, errOutputCh, data, narrative, live)
} }
pdfWG.Wait() pdfWG.Wait()