mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-23 00:32:55 +01:00
Merge pull request #51 from csaf-poc/provider_documentation
Add some code documentation
This commit is contained in:
commit
83299ebe8b
4 changed files with 58 additions and 5 deletions
|
|
@ -29,6 +29,8 @@ import (
|
||||||
|
|
||||||
const dateFormat = time.RFC3339
|
const dateFormat = time.RFC3339
|
||||||
|
|
||||||
|
// cleanFileName removes the "/" "\" charachters and replace the two or more
|
||||||
|
// occurences of "." with only one from the passed string.
|
||||||
func cleanFileName(s string) string {
|
func cleanFileName(s string) string {
|
||||||
s = strings.ReplaceAll(s, `/`, ``)
|
s = strings.ReplaceAll(s, `/`, ``)
|
||||||
s = strings.ReplaceAll(s, `\`, ``)
|
s = strings.ReplaceAll(s, `\`, ``)
|
||||||
|
|
@ -37,6 +39,10 @@ func cleanFileName(s string) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadCSAF loads the csaf file from the request, calls the "UploadLimter" function to
|
||||||
|
// set the upload limit size of the file and the "cleanFileName" to refine
|
||||||
|
// the filename. It returns the filename, file content in a buffer of bytes
|
||||||
|
// and an error.
|
||||||
func (c *controller) loadCSAF(r *http.Request) (string, []byte, error) {
|
func (c *controller) loadCSAF(r *http.Request) (string, []byte, error) {
|
||||||
file, handler, err := r.FormFile("csaf")
|
file, handler, err := r.FormFile("csaf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -123,6 +129,8 @@ func (c *controller) tlpParam(r *http.Request) (tlp, error) {
|
||||||
return "", fmt.Errorf("unsupported TLP type '%s'", t)
|
return "", fmt.Errorf("unsupported TLP type '%s'", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create calls the "ensureFolders" functions to create the directories and files.
|
||||||
|
// It returns a struct by success, otherwise an error.
|
||||||
func (c *controller) create(*http.Request) (interface{}, error) {
|
func (c *controller) create(*http.Request) (interface{}, error) {
|
||||||
if err := ensureFolders(c.cfg); err != nil {
|
if err := ensureFolders(c.cfg); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// The environment name, that contains the path to the config file.
|
||||||
configEnv = "CSAF_CONFIG"
|
configEnv = "CSAF_CONFIG"
|
||||||
defaultConfigPath = "/usr/lib/casf/config.toml"
|
defaultConfigPath = "/usr/lib/casf/config.toml" // Default path to the config file.
|
||||||
defaultFolder = "/var/www/"
|
defaultFolder = "/var/www/" // Default folder path.
|
||||||
defaultWeb = "/var/www/html"
|
defaultWeb = "/var/www/html" // Default web path.
|
||||||
defaultOpenPGPURL = "https://openpgp.circl.lu/pks/lookup?op=get&search=${FINGERPRINT}"
|
defaultOpenPGPURL = "https://openpgp.circl.lu/pks/lookup?op=get&search=${FINGERPRINT}" // Default OpenPGP URL.
|
||||||
defaultUploadLimit = 50 * 1024 * 1024
|
defaultUploadLimit = 50 * 1024 * 1024 // Default limit size of the uploaded file.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// configs contains the config values for the provider.
|
||||||
type config struct {
|
type config struct {
|
||||||
Password *string `toml:"password"`
|
Password *string `toml:"password"`
|
||||||
Key string `toml:"key"`
|
Key string `toml:"key"`
|
||||||
|
|
@ -56,6 +58,7 @@ const (
|
||||||
tlpRed tlp = "red"
|
tlpRed tlp = "red"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// valid returns true if the checked tlp matches one of the defined tlps.
|
||||||
func (t tlp) valid() bool {
|
func (t tlp) valid() bool {
|
||||||
switch t {
|
switch t {
|
||||||
case tlpCSAF, tlpWhite, tlpGreen, tlpAmber, tlpRed:
|
case tlpCSAF, tlpWhite, tlpGreen, tlpAmber, tlpRed:
|
||||||
|
|
@ -73,6 +76,8 @@ func (t *tlp) UnmarshalText(text []byte) error {
|
||||||
return fmt.Errorf("invalid config TLP value: %v", string(text))
|
return fmt.Errorf("invalid config TLP value: %v", string(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uploadLimiter returns a reader that reads from a given r reader but stops
|
||||||
|
// with EOF after the defined bytes in the "UploadLimit" config option.
|
||||||
func (cfg *config) uploadLimiter(r io.Reader) io.Reader {
|
func (cfg *config) uploadLimiter(r io.Reader) io.Reader {
|
||||||
// Zero or less means no upload limit.
|
// Zero or less means no upload limit.
|
||||||
if cfg.UploadLimit == nil || *cfg.UploadLimit < 1 {
|
if cfg.UploadLimit == nil || *cfg.UploadLimit < 1 {
|
||||||
|
|
@ -100,6 +105,8 @@ func (cfg *config) modelTLPs() []csaf.TLPLabel {
|
||||||
return tlps
|
return tlps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadCryptoKey loads the armored data into the key stored in the file specified by the
|
||||||
|
// "key" config value and return it with nil, otherwise an error.
|
||||||
func (cfg *config) loadCryptoKey() (*crypto.Key, error) {
|
func (cfg *config) loadCryptoKey() (*crypto.Key, error) {
|
||||||
f, err := os.Open(cfg.Key)
|
f, err := os.Open(cfg.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -109,11 +116,18 @@ func (cfg *config) loadCryptoKey() (*crypto.Key, error) {
|
||||||
return crypto.NewKeyFromArmoredReader(f)
|
return crypto.NewKeyFromArmoredReader(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkPassword compares the given hashed password with the plaintext in the "password" config value.
|
||||||
|
// It returns true if these matches or if the "password" config value is not set, otherwise false.
|
||||||
func (cfg *config) checkPassword(hash string) bool {
|
func (cfg *config) checkPassword(hash string) bool {
|
||||||
return cfg.Password == nil ||
|
return cfg.Password == nil ||
|
||||||
bcrypt.CompareHashAndPassword([]byte(hash), []byte(*cfg.Password)) == nil
|
bcrypt.CompareHashAndPassword([]byte(hash), []byte(*cfg.Password)) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadConfig extracts the config values from the config file. The path to the
|
||||||
|
// file is taken either from environment variable "CSAF_CONFIG" or from the
|
||||||
|
// defined default path in "defaultConfigPath".
|
||||||
|
// Default values are set in case some are missing in the file.
|
||||||
|
// It returns these values in a struct and nil if there is no error.
|
||||||
func loadConfig() (*config, error) {
|
func loadConfig() (*config, error) {
|
||||||
path := os.Getenv(configEnv)
|
path := os.Getenv(configEnv)
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,14 @@ func asMultiError(err error) multiError {
|
||||||
return multiError([]string{err.Error()})
|
return multiError([]string{err.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// controller contains the config values and the html templates.
|
||||||
type controller struct {
|
type controller struct {
|
||||||
cfg *config
|
cfg *config
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newController assigns the given configs to a controller variable and parses the html template
|
||||||
|
// if the config value "NoWebUI" is true. It returns the controller variable and nil, otherwise error.
|
||||||
func newController(cfg *config) (*controller, error) {
|
func newController(cfg *config) (*controller, error) {
|
||||||
|
|
||||||
c := controller{cfg: cfg}
|
c := controller{cfg: cfg}
|
||||||
|
|
@ -56,6 +59,8 @@ func newController(cfg *config) (*controller, error) {
|
||||||
return &c, nil
|
return &c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bind binds the paths with the corresponding http.handler and wraps it with the respective middleware,
|
||||||
|
// according to the "NoWebUI" config value.
|
||||||
func (c *controller) bind(pim *pathInfoMux) {
|
func (c *controller) bind(pim *pathInfoMux) {
|
||||||
if !c.cfg.NoWebUI {
|
if !c.cfg.NoWebUI {
|
||||||
pim.handleFunc("/", c.auth(c.index))
|
pim.handleFunc("/", c.auth(c.index))
|
||||||
|
|
@ -66,6 +71,9 @@ func (c *controller) bind(pim *pathInfoMux) {
|
||||||
pim.handleFunc("/api/create", c.auth(api(c.create)))
|
pim.handleFunc("/api/create", c.auth(api(c.create)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// auth wraps the given http.HandlerFunc and returns an new one after authenticating the
|
||||||
|
// password contained in the header "X-CSAF-PROVIDER-AUTH" with the "password" config value
|
||||||
|
// if set, otherwise returns the given http.HandlerFunc.
|
||||||
func (c *controller) auth(
|
func (c *controller) auth(
|
||||||
fn func(http.ResponseWriter, *http.Request),
|
fn func(http.ResponseWriter, *http.Request),
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
|
|
@ -83,6 +91,9 @@ func (c *controller) auth(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render sets the headers for the response. It applies the given template "tmpl" to
|
||||||
|
// the given object "arg" and writes the output to http.ResponseWriter.
|
||||||
|
// It logs a warning in case of error.
|
||||||
func (c *controller) render(rw http.ResponseWriter, tmpl string, arg interface{}) {
|
func (c *controller) render(rw http.ResponseWriter, tmpl string, arg interface{}) {
|
||||||
rw.Header().Set("Content-type", "text/html; charset=utf-8")
|
rw.Header().Set("Content-type", "text/html; charset=utf-8")
|
||||||
rw.Header().Set("X-Content-Type-Options", "nosniff")
|
rw.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
|
@ -91,17 +102,24 @@ func (c *controller) render(rw http.ResponseWriter, tmpl string, arg interface{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// failed constructs the error messages by calling "asMultiError" and calls "render"
|
||||||
|
// function to render the passed template and error object.
|
||||||
func (c *controller) failed(rw http.ResponseWriter, tmpl string, err error) {
|
func (c *controller) failed(rw http.ResponseWriter, tmpl string, err error) {
|
||||||
result := map[string]interface{}{"Error": asMultiError(err)}
|
result := map[string]interface{}{"Error": asMultiError(err)}
|
||||||
c.render(rw, tmpl, result)
|
c.render(rw, tmpl, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// index calls the "render" function and passes the "index.html" and c.cfg to it.
|
||||||
func (c *controller) index(rw http.ResponseWriter, r *http.Request) {
|
func (c *controller) index(rw http.ResponseWriter, r *http.Request) {
|
||||||
c.render(rw, "index.html", map[string]interface{}{
|
c.render(rw, "index.html", map[string]interface{}{
|
||||||
"Config": c.cfg,
|
"Config": c.cfg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// web executes the given function "fn", calls the "render" function and passes
|
||||||
|
// the result content from "fn", the given template and the http.ResponseWriter to it
|
||||||
|
// in case of no error occurred, otherwise calls the "failed" function and passes the given
|
||||||
|
// template and the error from "fn".
|
||||||
func (c *controller) web(
|
func (c *controller) web(
|
||||||
fn func(*http.Request) (interface{}, error),
|
fn func(*http.Request) (interface{}, error),
|
||||||
tmpl string,
|
tmpl string,
|
||||||
|
|
@ -116,6 +134,8 @@ func (c *controller) web(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeJSON sets the header for the response and writes the JSON encoding of the given "content".
|
||||||
|
// It logs out an error message in case of an error.
|
||||||
func writeJSON(rw http.ResponseWriter, content interface{}, code int) {
|
func writeJSON(rw http.ResponseWriter, content interface{}, code int) {
|
||||||
rw.Header().Set("Content-type", "application/json; charset=utf-8")
|
rw.Header().Set("Content-type", "application/json; charset=utf-8")
|
||||||
rw.Header().Set("X-Content-Type-Options", "nosniff")
|
rw.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"github.com/csaf-poc/csaf_distribution/util"
|
"github.com/csaf-poc/csaf_distribution/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ensureFolders initializes the paths and call functions to create
|
||||||
|
// the directories and files.
|
||||||
func ensureFolders(c *config) error {
|
func ensureFolders(c *config) error {
|
||||||
|
|
||||||
wellknown := filepath.Join(c.Web, ".well-known")
|
wellknown := filepath.Join(c.Web, ".well-known")
|
||||||
|
|
@ -38,6 +40,8 @@ func ensureFolders(c *config) error {
|
||||||
return createSecurity(c, wellknown)
|
return createSecurity(c, wellknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createWellknown creates ".well-known" directory if not exist and returns nil.
|
||||||
|
// An error is returned if the it is not a directory.
|
||||||
func createWellknown(wellknown string) error {
|
func createWellknown(wellknown string) error {
|
||||||
st, err := os.Stat(wellknown)
|
st, err := os.Stat(wellknown)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -52,6 +56,10 @@ func createWellknown(wellknown string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createFeedFolders creates the feed folders according to the tlp values
|
||||||
|
// in the "tlps" config option if they do not already exist.
|
||||||
|
// No creation for the "csaf" option will be done.
|
||||||
|
// It creates also symbolic links to feed folders.
|
||||||
func createFeedFolders(c *config, wellknown string) error {
|
func createFeedFolders(c *config, wellknown string) error {
|
||||||
for _, t := range c.TLPs {
|
for _, t := range c.TLPs {
|
||||||
if t == tlpCSAF {
|
if t == tlpCSAF {
|
||||||
|
|
@ -75,6 +83,8 @@ func createFeedFolders(c *config, wellknown string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createSecurity creats the "security.txt" file if does not exist
|
||||||
|
// and writes the CSAF field inside the file.
|
||||||
func createSecurity(c *config, wellknown string) error {
|
func createSecurity(c *config, wellknown string) error {
|
||||||
security := filepath.Join(wellknown, "security.txt")
|
security := filepath.Join(wellknown, "security.txt")
|
||||||
if _, err := os.Stat(security); err != nil {
|
if _, err := os.Stat(security); err != nil {
|
||||||
|
|
@ -93,6 +103,7 @@ func createSecurity(c *config, wellknown string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createProviderMetadata creates the provider-metadata.json file if does not exist.
|
||||||
func createProviderMetadata(c *config, wellknownCSAF string) error {
|
func createProviderMetadata(c *config, wellknownCSAF string) error {
|
||||||
path := filepath.Join(wellknownCSAF, "provider-metadata.json")
|
path := filepath.Join(wellknownCSAF, "provider-metadata.json")
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue