main.go 5.66 KB
Newer Older
1
2
3
package main

import (
4
	"encoding/json"
Sietse Ringers's avatar
Sietse Ringers committed
5
	"io/ioutil"
6
	"os"
7
8
	"path/filepath"
	"strings"
9

10
	"github.com/Sirupsen/logrus"
11
	"github.com/go-errors/errors"
Sietse Ringers's avatar
Sietse Ringers committed
12
13
	"github.com/privacybydesign/irmago/server"
	"github.com/privacybydesign/irmago/server/irmaserver"
14
15
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
16
17
)

Sietse Ringers's avatar
Sietse Ringers committed
18
var logger = logrus.StandardLogger()
19
20
var conf *irmaserver.Configuration

21
func main() {
22
23
24
25
26
27
28
29
30
31
32
33
	var cmd = &cobra.Command{
		Use:   "irmaserver",
		Short: "IRMA server for verifying and issuing attributes",
		Run: func(command *cobra.Command, args []string) {
			if err := configure(); err != nil {
				die(errors.WrapPrefix(err, "Failed to configure server", 0))
			}
			if err := irmaserver.Start(conf); err != nil {
				die(errors.WrapPrefix(err, "Failed to start server", 0))
			}
		},
	}
34

Sietse Ringers's avatar
Sietse Ringers committed
35
36
37
38
39
	logger.Level = logrus.InfoLevel
	logger.SetFormatter(&logrus.TextFormatter{
		FullTimestamp: true,
	})

40
41
	if err := setFlags(cmd); err != nil {
		die(errors.WrapPrefix(err, "Failed to attach flags", 0))
42
43
	}

44
45
	if err := cmd.Execute(); err != nil {
		die(errors.WrapPrefix(err, "Failed to execute command", 0))
46
	}
47
48
49
}

func die(err *errors.Error) {
Sietse Ringers's avatar
Sietse Ringers committed
50
	logger.Error(err.Error())
Sietse Ringers's avatar
Sietse Ringers committed
51
	logger.Trace(string(err.Stack()))
52
53
54
55
56
57
58
	os.Exit(1)
}

func setFlags(cmd *cobra.Command) error {
	flags := cmd.Flags()
	flags.SortFlags = false

59
	flags.StringP("config", "c", "", "Path to configuration file")
60
61
62
63
64
65
66
67
	flags.StringP("irmaconf", "i", "./irma_configuration", "path to irma_configuration")
	flags.StringP("privatekeys", "k", "", "path to IRMA private keys")
	flags.StringP("jwtissuer", "j", "irmaserver", "JWT issuer")
	flags.StringP("jwtprivatekey", "w", "", "JWT private key or path to it")
	flags.IntP("port", "p", 8088, "Port at which to listen")
	flags.Bool("noauth", false, "Whether or not to authenticate requestors")
	flags.String("requestors", "", "Requestor configuration (in JSON)")

68
69
70
71
72
73
74
	flags.StringSlice("disclosing", nil, "Comma-separated list of attributes that all requestors may verify")
	flags.StringSlice("signing", nil, "Comma-separated list of attributes that all requestors may request in signatures")
	flags.StringSlice("issuing", nil, "Comma-separated list of attributes that all requestors may issue")
	flags.Lookup("disclosing").NoOptDefVal = "*"
	flags.Lookup("signing").NoOptDefVal = "*"
	flags.Lookup("issuing").NoOptDefVal = "*"

Sietse Ringers's avatar
Sietse Ringers committed
75
76
77
	flags.BoolP("verbose", "v", false, "verbose")
	flags.Bool("vverbose", false, "more verbose")
	flags.BoolP("quiet", "q", false, "quiet")
78
79
80
81

	// Environment variables
	viper.SetEnvPrefix("IRMASERVER")
	viper.AutomaticEnv()
82

Sietse Ringers's avatar
Sietse Ringers committed
83
84
85
86
	return viper.BindPFlags(flags)
}

func configure() error {
87
88
89
90
91
92
93
94
95
96
97
98
	// Locate and read configuration file
	confpath := viper.GetString("config")
	if confpath != "" {
		dir, file := filepath.Dir(confpath), filepath.Base(confpath)
		viper.SetConfigName(strings.TrimSuffix(file, filepath.Ext(file)))
		viper.AddConfigPath(dir)
	} else {
		viper.SetConfigName("irmaserver")
		viper.AddConfigPath(".")
		viper.AddConfigPath("/etc/irmaserver/")
		viper.AddConfigPath("$HOME/.irmaserver")
	}
Sietse Ringers's avatar
Sietse Ringers committed
99
	err := viper.ReadInConfig() // Hold error checking until we know how much of it to log
100
101

	// Set log level
Sietse Ringers's avatar
Sietse Ringers committed
102
103
104
105
106
107
108
109
110
111
112
113
	if viper.GetBool("verbose") {
		logger.Level = logrus.DebugLevel
	}
	if viper.GetBool("vverbose") {
		logger.Level = logrus.TraceLevel
	}
	if viper.GetBool("quiet") {
		logger.Out = ioutil.Discard
	}

	logger.Debugf("Configuring")
	if err != nil {
114
115
116
117
118
		if _, notfound := err.(viper.ConfigFileNotFoundError); notfound {
			logger.Info("No configuration file found")
		} else {
			die(errors.WrapPrefix(err, "Failed to unmarshal configuration file at "+viper.ConfigFileUsed(), 0))
		}
119
	} else {
Sietse Ringers's avatar
Sietse Ringers committed
120
		logger.Info("Config file: ", viper.ConfigFileUsed())
121
122
123
124
	}

	// Read configuration from flags and/or environmental variables
	conf = &irmaserver.Configuration{
Sietse Ringers's avatar
Sietse Ringers committed
125
		Configuration: &server.Configuration{
126
127
			IrmaConfigurationPath: viper.GetString("irmaconf"),
			IssuerPrivateKeysPath: viper.GetString("privatekeys"),
Sietse Ringers's avatar
Sietse Ringers committed
128
			Logger:                logger,
129
		},
130
131
132
133
134
135
		Port: viper.GetInt("port"),
		DisableRequestorAuthentication: viper.GetBool("noauth"),
		Requestors:                     make(map[string]irmaserver.Requestor),
		GlobalPermissions:              irmaserver.Permissions{},
		JwtIssuer:                      viper.GetString("jwtissuer"),
		JwtPrivateKey:                  viper.GetString("jwtprivatekey"),
Sietse Ringers's avatar
Sietse Ringers committed
136
137
		Verbose:                        viper.GetBool("verbose"),
		VVerbose:                       viper.GetBool("vverbose"),
138
139
	}

140
141
	// Handle global permissions
	if len(viper.GetStringMap("permissions")) > 0 { // First read config file
142
		if err := viper.UnmarshalKey("permissions", &conf.GlobalPermissions); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
143
			return errors.WrapPrefix(err, "Failed to unmarshal permissions from config file", 0)
144
145
		}
	}
146
147
148
149
150
151
152
	handlePermission(&conf.GlobalPermissions.Disclosing, "disclosing") // Read flag or env var
	handlePermission(&conf.GlobalPermissions.Signing, "signing")
	handlePermission(&conf.GlobalPermissions.Issuing, "issuing")

	// Handle requestors
	if len(viper.GetStringMap("requestors")) > 0 { // First read config file
		if err := viper.UnmarshalKey("requestors", &conf.Requestors); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
153
			return errors.WrapPrefix(err, "Failed to unmarshal requestors from config file", 0)
154
155
156
		}
	}
	requestors := viper.GetString("requestors") // Read flag or env var
157
158
	if len(requestors) > 0 {
		if err := json.Unmarshal([]byte(requestors), &conf.Requestors); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
159
			return errors.WrapPrefix(err, "Failed to unmarshal requestors from json", 0)
160
161
162
163
		}
	}

	bts, _ := json.MarshalIndent(conf, "", "   ")
Sietse Ringers's avatar
Sietse Ringers committed
164
165
	logger.Debug(string(bts), "\n")
	logger.Debug("Done configuring")
166
167

	return nil
168
}
169
170
171
172
173
174
175
176
177
178

func handlePermission(conf *[]string, typ string) {
	if viper.GetString(typ) == "*" {
		*conf = []string{"*"}
	}
	perms := viper.GetStringSlice(typ)
	if len(perms) > 0 {
		*conf = perms
	}
}