Commit ce17e1a0 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Validate configuration when starting server

parent d11d0139
...@@ -80,14 +80,26 @@ func (hauth *HmacAuthenticator) Authenticate( ...@@ -80,14 +80,26 @@ func (hauth *HmacAuthenticator) Authenticate(
func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) error { func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) error {
if requestor.AuthenticationKey == "" { if requestor.AuthenticationKey == "" {
return errors.Errorf("Requestor %s had no authentication key") return errors.Errorf("Requestor %s has no authentication key", name)
} }
if bts, err := base64.StdEncoding.DecodeString(requestor.AuthenticationKey); err != nil {
return err var err error
} else { var bts []byte
hauth.hmackeys[name] = bts // We accept any of the base64 encodings
return nil encodings := []*base64.Encoding{base64.StdEncoding, base64.RawStdEncoding, base64.URLEncoding, base64.RawURLEncoding}
for _, encoding := range encodings {
err = nil
if bts, err = encoding.DecodeString(requestor.AuthenticationKey); err == nil {
break
}
} }
if err != nil {
return errors.WrapPrefix(err, "Failed to base64 decode hmac key of requestor "+name, 0)
}
hauth.hmackeys[name] = bts
return nil
} }
func (pkauth *PublicKeyAuthenticator) Authenticate( func (pkauth *PublicKeyAuthenticator) Authenticate(
...@@ -140,7 +152,7 @@ func (pskauth *PresharedKeyAuthenticator) Authenticate( ...@@ -140,7 +152,7 @@ func (pskauth *PresharedKeyAuthenticator) Authenticate(
func (pskauth *PresharedKeyAuthenticator) Initialize(name string, requestor Requestor) error { func (pskauth *PresharedKeyAuthenticator) Initialize(name string, requestor Requestor) error {
if requestor.AuthenticationKey == "" { if requestor.AuthenticationKey == "" {
return errors.Errorf("Requestor %s had no authentication key") return errors.Errorf("Requestor %s has no authentication key", name)
} }
pskauth.presharedkeys[requestor.AuthenticationKey] = name pskauth.presharedkeys[requestor.AuthenticationKey] = name
return nil return nil
......
...@@ -23,7 +23,7 @@ type Configuration struct { ...@@ -23,7 +23,7 @@ type Configuration struct {
// server configuration before the server accepts it. // server configuration before the server accepts it.
DisableRequestorAuthentication bool `json:"noauth" mapstructure:"noauth"` DisableRequestorAuthentication bool `json:"noauth" mapstructure:"noauth"`
// Address to listen at. May include port (e.g. 0.0.0.0:1234) but then Port must be 0. // Address to listen at
ListenAddress string `json:"listenaddr" mapstructure:"listenaddr"` ListenAddress string `json:"listenaddr" mapstructure:"listenaddr"`
// Port to listen at // Port to listen at
Port int `json:"port" mapstructure:"port"` Port int `json:"port" mapstructure:"port"`
...@@ -89,13 +89,6 @@ func (conf *Configuration) CanIssue(requestor string, creds []*irma.CredentialRe ...@@ -89,13 +89,6 @@ func (conf *Configuration) CanIssue(requestor string, creds []*irma.CredentialRe
return true, "" return true, ""
} }
func (conf *Configuration) listenAddress() string {
if conf.Port == 0 {
return conf.ListenAddress
}
return fmt.Sprintf("%s:%d", conf.ListenAddress, conf.Port)
}
// CanVerifyOrSign returns whether or not the specified requestor may use the selected attributes // CanVerifyOrSign returns whether or not the specified requestor may use the selected attributes
// in any of the supported session types. // in any of the supported session types.
func (conf *Configuration) CanVerifyOrSign(requestor string, action irma.Action, disjunctions irma.AttributeDisjunctionList) (bool, string) { func (conf *Configuration) CanVerifyOrSign(requestor string, action irma.Action, disjunctions irma.AttributeDisjunctionList) (bool, string) {
...@@ -148,7 +141,7 @@ func (conf *Configuration) initialize() error { ...@@ -148,7 +141,7 @@ func (conf *Configuration) initialize() error {
for name, requestor := range conf.Requestors { for name, requestor := range conf.Requestors {
authenticator, ok := authenticators[requestor.AuthenticationMethod] authenticator, ok := authenticators[requestor.AuthenticationMethod]
if !ok { if !ok {
return errors.Errorf("Requestor %s has unsupported authentication type") return errors.Errorf("Requestor %s has unsupported authentication type", name)
} }
if err := authenticator.Initialize(name, requestor); err != nil { if err := authenticator.Initialize(name, requestor); err != nil {
return err return err
...@@ -156,6 +149,14 @@ func (conf *Configuration) initialize() error { ...@@ -156,6 +149,14 @@ func (conf *Configuration) initialize() error {
} }
} }
if conf.Port <= 0 || conf.Port > 65535 {
return errors.Errorf("Port must be between 1 and 65535 (was %d)", conf.Port)
}
if err := conf.validatePermissions(); err != nil {
return err
}
if conf.URL != "" { if conf.URL != "" {
if !strings.HasSuffix(conf.URL, "/") { if !strings.HasSuffix(conf.URL, "/") {
conf.URL = conf.URL + "/" conf.URL = conf.URL + "/"
...@@ -169,6 +170,75 @@ func (conf *Configuration) initialize() error { ...@@ -169,6 +170,75 @@ func (conf *Configuration) initialize() error {
return nil return nil
} }
func (conf *Configuration) validatePermissions() error {
if conf.DisableRequestorAuthentication && len(conf.Requestors) != 0 {
return errors.New("Requestors must not be configured when requestor authentication is disabled")
}
errs := conf.validatePermissionSet("Global", conf.GlobalPermissions)
for name, requestor := range conf.Requestors {
errs = append(errs, conf.validatePermissionSet("Requestor "+name, requestor.Permissions)...)
}
if len(errs) != 0 {
return errors.New("Errors encountered in permissions:\n" + strings.Join(errs, "\n"))
}
return nil
}
func (conf *Configuration) validatePermissionSet(requestor string, requestorperms Permissions) []string {
var errs []string
perms := map[string][]string{
"issuing": requestorperms.Issuing,
"signing": requestorperms.Signing,
"disclosing": requestorperms.Disclosing,
}
permissionlength := map[string]int{"issuing": 3, "signing": 4, "disclosing": 4}
for typ, typeperms := range perms {
for _, permission := range typeperms {
parts := strings.Split(permission, ".")
if parts[len(parts)-1] == "*" {
if len(parts) > permissionlength[typ] {
errs = append(errs, fmt.Sprintf("%s %s permission '%s' should have at most %d parts", requestor, typ, permission, permissionlength[typ]))
}
} else {
if len(parts) != permissionlength[typ] {
errs = append(errs, fmt.Sprintf("%s %s permission '%s' should have %d parts", requestor, typ, permission, permissionlength[typ]))
}
}
if len(parts) > 0 && parts[0] != "*" {
if conf.IrmaConfiguration.SchemeManagers[irma.NewSchemeManagerIdentifier(parts[0])] == nil {
errs = append(errs, fmt.Sprintf("%s %s permission '%s': unknown scheme", requestor, typ, permission))
continue // no sense in checking if issuer, credtype or attr type are known; they won't be
}
}
if len(parts) > 1 && parts[1] != "*" {
id := irma.NewIssuerIdentifier(strings.Join(parts[:2], "."))
if conf.IrmaConfiguration.Issuers[id] == nil {
errs = append(errs, fmt.Sprintf("%s %s permission '%s': unknown issuer", requestor, typ, permission))
continue
}
}
if len(parts) > 2 && parts[2] != "*" {
id := irma.NewCredentialTypeIdentifier(strings.Join(parts[:3], "."))
if conf.IrmaConfiguration.CredentialTypes[id] == nil {
errs = append(errs, fmt.Sprintf("%s %s permission '%s': unknown credential type", requestor, typ, permission))
continue
}
}
if len(parts) > 3 && parts[3] != "*" {
id := irma.NewAttributeTypeIdentifier(strings.Join(parts[:4], "."))
if conf.IrmaConfiguration.AttributeTypes[id] == nil {
errs = append(errs, fmt.Sprintf("%s %s permission '%s': unknown attribute type", requestor, typ, permission))
continue
}
}
}
}
return errs
}
func (conf *Configuration) readPrivateKey() error { func (conf *Configuration) readPrivateKey() error {
if conf.JwtPrivateKey == "" { if conf.JwtPrivateKey == "" {
return nil return nil
......
...@@ -4,7 +4,6 @@ package main ...@@ -4,7 +4,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
...@@ -48,9 +47,11 @@ func main() { ...@@ -48,9 +47,11 @@ func main() {
} }
func die(err *errors.Error) { func die(err *errors.Error) {
logger.Error(err.Error()) msg := err.Error()
logger.Trace(string(err.Stack())) if logger.IsLevelEnabled(logrus.TraceLevel) {
os.Exit(1) msg += "\nStack trace:\n" + string(err.Stack())
}
logger.Fatal(msg)
} }
func setFlags(cmd *cobra.Command) error { func setFlags(cmd *cobra.Command) error {
...@@ -80,8 +81,8 @@ func setFlags(cmd *cobra.Command) error { ...@@ -80,8 +81,8 @@ func setFlags(cmd *cobra.Command) error {
flags.Bool("noauth", false, "Whether or not to authenticate requestors") flags.Bool("noauth", false, "Whether or not to authenticate requestors")
flags.String("requestors", "", "Requestor configuration (in JSON)") flags.String("requestors", "", "Requestor configuration (in JSON)")
flags.StringSlice("disclose", []string{"*"}, "Comma-separated list of attributes that all requestors may verify") flags.StringSlice("disclose", nil, "Comma-separated list of attributes that all requestors may verify")
flags.StringSlice("sign", []string{"*"}, "Comma-separated list of attributes that all requestors may request in signatures") flags.StringSlice("sign", nil, "Comma-separated list of attributes that all requestors may request in signatures")
flags.StringSlice("issue", nil, "Comma-separated list of attributes that all requestors may issue") flags.StringSlice("issue", nil, "Comma-separated list of attributes that all requestors may issue")
flags.CountP("verbose", "v", "verbose (repeatable)") flags.CountP("verbose", "v", "verbose (repeatable)")
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
package irmaserver package irmaserver
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"time" "time"
...@@ -27,7 +28,7 @@ func Start(config *Configuration) error { ...@@ -27,7 +28,7 @@ func Start(config *Configuration) error {
} }
// Start server // Start server
addr := config.listenAddress() addr := fmt.Sprintf("%s:%d", conf.ListenAddress, conf.Port)
config.Logger.Info("Listening at ", addr) config.Logger.Info("Listening at ", addr)
s = &http.Server{Addr: addr, Handler: handler} s = &http.Server{Addr: addr, Handler: handler}
err = s.ListenAndServe() err = s.ListenAndServe()
...@@ -46,12 +47,12 @@ func Stop() { ...@@ -46,12 +47,12 @@ func Stop() {
// and IRMA client messages. // and IRMA client messages.
func Handler(config *Configuration) (http.Handler, error) { func Handler(config *Configuration) (http.Handler, error) {
conf = config conf = config
if err := conf.initialize(); err != nil {
return nil, server.LogError(err)
}
if err := irmarequestor.Initialize(conf.Configuration); err != nil { if err := irmarequestor.Initialize(conf.Configuration); err != nil {
return nil, err return nil, err
} }
if err := conf.initialize(); err != nil {
return nil, err
}
router := chi.NewRouter() router := chi.NewRouter()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment