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

Support for separate listen address and port for requestor and client servers

parent ce17e1a0
...@@ -31,7 +31,7 @@ func StartIrmaServer(configuration *irmaserver.Configuration) { ...@@ -31,7 +31,7 @@ func StartIrmaServer(configuration *irmaserver.Configuration) {
} }
func StopIrmaServer() { func StopIrmaServer() {
irmaserver.Stop() _ = irmaserver.Stop()
} }
var IrmaServerConfiguration = &irmaserver.Configuration{ var IrmaServerConfiguration = &irmaserver.Configuration{
......
...@@ -2,6 +2,7 @@ package server ...@@ -2,6 +2,7 @@ package server
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net" "net"
"net/http" "net/http"
"os" "os"
...@@ -65,7 +66,7 @@ const ( ...@@ -65,7 +66,7 @@ const (
// RemoteError converts an error and an explaining message to an *irma.RemoteError. // RemoteError converts an error and an explaining message to an *irma.RemoteError.
func RemoteError(err Error, message string) *irma.RemoteError { func RemoteError(err Error, message string) *irma.RemoteError {
stack := string(debug.Stack()) stack := string(debug.Stack())
Logger.Warn("Session error: %d %s %s\n%s", err.Status, err.Type, message, stack) Logger.Warnf("Session error: %d %s %s\n%s", err.Status, err.Type, message, stack)
return &irma.RemoteError{ return &irma.RemoteError{
Status: err.Status, Status: err.Status,
Description: err.Description, Description: err.Description,
...@@ -210,24 +211,26 @@ func Verbosity(level int) logrus.Level { ...@@ -210,24 +211,26 @@ func Verbosity(level int) logrus.Level {
} }
} }
func LogError(err error) error { func log(level logrus.Level, err error) error {
Logger.Error(err.Error()) writer := Logger.WriterLevel(level)
if e, ok := err.(*errors.Error); ok { if e, ok := err.(*errors.Error); ok && Logger.IsLevelEnabled(logrus.TraceLevel) {
Logger.Error(e.ErrorStack()) _, _ = writer.Write([]byte(e.ErrorStack()))
} else { } else {
Logger.Errorf("%s %s", reflect.TypeOf(err).String(), err.Error()) _, _ = writer.Write([]byte(fmt.Sprintf("%s %s", reflect.TypeOf(err).String(), err.Error())))
} }
return err return err
} }
func LogFatal(err error) error {
return log(logrus.FatalLevel, err)
}
func LogError(err error) error {
return log(logrus.ErrorLevel, err)
}
func LogWarning(err error) error { func LogWarning(err error) error {
Logger.Warn(err.Error()) return log(logrus.WarnLevel, err)
if e, ok := err.(*errors.Error); ok {
Logger.Warn(e.ErrorStack())
} else {
Logger.Warn("%s %s", reflect.TypeOf(err).String(), err.Error())
}
return err
} }
func ToJson(o interface{}) string { func ToJson(o interface{}) string {
......
...@@ -28,6 +28,11 @@ type Configuration struct { ...@@ -28,6 +28,11 @@ type Configuration struct {
// Port to listen at // Port to listen at
Port int `json:"port" mapstructure:"port"` Port int `json:"port" mapstructure:"port"`
// If specified, start a separate server for the IRMA app at his port
ClientPort int `json:"clientport" mapstructure:"clientport"`
// If clientport is specified, the server for the IRMA app listens at this address
ClientListenAddress string `json:"clientlistenaddr" mapstructure:"clientlistenaddr"`
// Requestor-specific permission and authentication configuration // Requestor-specific permission and authentication configuration
RequestorsString string `json:"-" mapstructure:"requestors"` RequestorsString string `json:"-" mapstructure:"requestors"`
Requestors map[string]Requestor `json:"requestors"` Requestors map[string]Requestor `json:"requestors"`
...@@ -153,6 +158,16 @@ func (conf *Configuration) initialize() error { ...@@ -153,6 +158,16 @@ func (conf *Configuration) initialize() error {
return errors.Errorf("Port must be between 1 and 65535 (was %d)", conf.Port) return errors.Errorf("Port must be between 1 and 65535 (was %d)", conf.Port)
} }
if conf.ClientPort != 0 && conf.ClientPort == conf.Port {
return errors.New("If clientport is given it must be different from port")
}
if conf.ClientPort < 0 || conf.ClientPort > 65535 {
return errors.Errorf("clientport must be between 0 and 65535 (was %d)", conf.ClientPort)
}
if conf.ClientListenAddress != "" && conf.ClientPort == 0 {
return errors.New("clientlistenaddr must be combined with a nonzero clientport")
}
if err := conf.validatePermissions(); err != nil { if err := conf.validatePermissions(); err != nil {
return err return err
} }
...@@ -163,7 +178,11 @@ func (conf *Configuration) initialize() error { ...@@ -163,7 +178,11 @@ func (conf *Configuration) initialize() error {
} }
conf.URL = conf.URL + "irma/" conf.URL = conf.URL + "irma/"
// replace "port" in url with actual port // replace "port" in url with actual port
replace := "$1:" + strconv.Itoa(conf.Port) port := conf.ClientPort
if port == 0 {
port = conf.Port
}
replace := "$1:" + strconv.Itoa(port)
conf.URL = string(regexp.MustCompile("(https?://[^/]*):port").ReplaceAll([]byte(conf.URL), []byte(replace))) conf.URL = string(regexp.MustCompile("(https?://[^/]*):port").ReplaceAll([]byte(conf.URL), []byte(replace)))
} }
...@@ -261,6 +280,10 @@ func (conf *Configuration) readPrivateKey() error { ...@@ -261,6 +280,10 @@ func (conf *Configuration) readPrivateKey() error {
return err return err
} }
func (conf *Configuration) separateClientServer() bool {
return conf.ClientPort != 0
}
// Return true iff query equals an element of strings. // Return true iff query equals an element of strings.
func contains(strings []string, query string) bool { func contains(strings []string, query string) bool {
for _, s := range strings { for _, s := range strings {
......
...@@ -78,6 +78,8 @@ func setFlags(cmd *cobra.Command) error { ...@@ -78,6 +78,8 @@ func setFlags(cmd *cobra.Command) error {
flags.StringP("url", "u", defaulturl, "External URL to server to which the IRMA client connects") flags.StringP("url", "u", defaulturl, "External URL to server to which the IRMA client connects")
flags.StringP("listenaddr", "l", "0.0.0.0", "Address at which to listen") flags.StringP("listenaddr", "l", "0.0.0.0", "Address at which to listen")
flags.IntP("port", "p", 8088, "Port at which to listen") flags.IntP("port", "p", 8088, "Port at which to listen")
flags.Int("clientport", 0, "If specified, start a separate server for the IRMA app at his port")
flags.String("clientlistenaddr", "0.0.0.0", "Address at which server for IRMA app listens")
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)")
...@@ -137,8 +139,10 @@ func configure() error { ...@@ -137,8 +139,10 @@ func configure() error {
URL: viper.GetString("url"), URL: viper.GetString("url"),
Logger: logger, Logger: logger,
}, },
ListenAddress: viper.GetString("listenaddr"), ListenAddress: viper.GetString("listenaddr"),
Port: viper.GetInt("port"), Port: viper.GetInt("port"),
ClientListenAddress: viper.GetString("clientlistenaddr"),
ClientPort: viper.GetInt("clientport"),
DisableRequestorAuthentication: viper.GetBool("noauth"), DisableRequestorAuthentication: viper.GetBool("noauth"),
Requestors: make(map[string]irmaserver.Requestor), Requestors: make(map[string]irmaserver.Requestor),
GlobalPermissions: irmaserver.Permissions{}, GlobalPermissions: irmaserver.Permissions{},
......
...@@ -16,54 +16,101 @@ import ( ...@@ -16,54 +16,101 @@ import (
) )
var ( var (
s *http.Server serv, clientserv *http.Server
conf *Configuration conf *Configuration
) )
// Start the server. If successful then it will not return until Stop() is called. // Start the server. If successful then it will not return until Stop() is called.
func Start(config *Configuration) error { func Start(config *Configuration) error {
handler, err := Handler(config) if err := Initialize(config); err != nil {
if err != nil {
return err return err
} }
// Start server // Start server(s)
addr := fmt.Sprintf("%s:%d", conf.ListenAddress, conf.Port) if conf.separateClientServer() {
config.Logger.Info("Listening at ", addr) go startClientServer()
s = &http.Server{Addr: addr, Handler: handler}
err = s.ListenAndServe()
if err == http.ErrServerClosed {
return nil // Server was closed normally
} }
startRequestorServer()
return server.LogError(err) return nil
} }
func Stop() { func startRequestorServer() {
s.Close() serv = &http.Server{}
startServer(serv, Handler(), "Server", conf.ListenAddress, conf.Port)
} }
// Handler returns a http.Handler that handles all IRMA requestor messages func startClientServer() {
// and IRMA client messages. clientserv = &http.Server{}
func Handler(config *Configuration) (http.Handler, error) { startServer(clientserv, ClientHandler(), "Client server", conf.ClientListenAddress, conf.ClientPort)
}
func startServer(s *http.Server, handler http.Handler, name, addr string, port int) {
fulladdr := fmt.Sprintf("%s:%d", addr, port)
conf.Logger.Info(name, " listening at ", fulladdr)
s.Addr = fulladdr
s.Handler = handler
if err := s.ListenAndServe(); err != http.ErrServerClosed {
_ = server.LogFatal(err)
}
}
func Stop() error {
var err1, err2 error
// Even if closing serv fails, we want to try closing clientserv
err1 = serv.Close()
if clientserv != nil {
err2 = clientserv.Close()
}
// Now check errors
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
return nil
}
func Initialize(config *Configuration) error {
conf = config conf = config
if err := irmarequestor.Initialize(conf.Configuration); err != nil { if err := irmarequestor.Initialize(conf.Configuration); err != nil {
return nil, err return err
} }
if err := conf.initialize(); err != nil { if err := conf.initialize(); err != nil {
return nil, err return err
} }
return nil
}
func ClientHandler() http.Handler {
router := chi.NewRouter() router := chi.NewRouter()
router.Use(cors.New(cors.Options{ router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"*"}, AllowedOrigins: []string{"*"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"}, AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodDelete}, AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodDelete},
}).Handler) }).Handler)
// Mount server for irmaclient
router.Mount("/irma/", irmarequestor.HttpHandlerFunc()) router.Mount("/irma/", irmarequestor.HttpHandlerFunc())
return router
}
// Handler returns a http.Handler that handles all IRMA requestor messages
// and IRMA client messages.
func Handler() http.Handler {
router := chi.NewRouter()
router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodDelete},
}).Handler)
if !conf.separateClientServer() {
// Mount server for irmaclient
router.Mount("/irma/", irmarequestor.HttpHandlerFunc())
}
// Server routes // Server routes
router.Post("/session", handleCreate) router.Post("/session", handleCreate)
...@@ -75,7 +122,7 @@ func Handler(config *Configuration) (http.Handler, error) { ...@@ -75,7 +122,7 @@ func Handler(config *Configuration) (http.Handler, error) {
router.Get("/session/{token}/result-jwt", handleJwtResult) router.Get("/session/{token}/result-jwt", handleJwtResult)
router.Get("/session/{token}/getproof", handleJwtProofs) // irma_api_server-compatible JWT router.Get("/session/{token}/getproof", handleJwtProofs) // irma_api_server-compatible JWT
return router, nil return router
} }
func handleCreate(w http.ResponseWriter, r *http.Request) { func handleCreate(w http.ResponseWriter, r *http.Request) {
......
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