conf.go 5.92 KB
Newer Older
1
2
3
4
5
6
7
8
package irmaserver

import (
	"crypto/rsa"
	"io/ioutil"
	"strings"

	"github.com/dgrijalva/jwt-go"
9
	"github.com/go-errors/errors"
10
11
12
13
14
15
16
17
18
19
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/fs"
	"github.com/privacybydesign/irmago/server"
)

type Configuration struct {
	*server.Configuration

	Port int

Sietse Ringers's avatar
Sietse Ringers committed
20
21
22
	// Whether or not incoming session requests should be authenticated. If false, anyone
	// can submit session requests. If true, the request is first authenticated against the
	// server configuration before the server accepts it.
23
	AuthenticateRequestors bool
Sietse Ringers's avatar
Sietse Ringers committed
24
25
	Requestors             map[string]Requestor // Requestor-specific permission and authentication configuration
	GlobalPermissions      Permissions          // Disclosing, signing or issuance permissions that apply to all requestors
26

27
28
	JwtIssuer     string // Used in the "iss" field of result JWTs from /result-jwt and /getproof
	JwtPrivateKey string // Private key to sign result JWTs with. If absent, /result-jwt and /getproof are disabled.
29

30
	jwtPrivateKey *rsa.PrivateKey
31
32
}

Sietse Ringers's avatar
Sietse Ringers committed
33
// Permissions specify which attributes or credential a requestor may verify or issue.
34
35
36
37
38
39
type Permissions struct {
	Disclosing []string
	Signing    []string
	Issuing    []string
}

Sietse Ringers's avatar
Sietse Ringers committed
40
41
// Requestor contains all configuration (disclosure or verification permissions and authentication)
// for a requestor.
42
43
44
45
46
47
48
type Requestor struct {
	Permissions

	AuthenticationMethod AuthenticationMethod
	AuthenticationKey    string
}

Sietse Ringers's avatar
Sietse Ringers committed
49
50
51
52
// CanIssue returns whether or not the specified requestor may issue the specified credentials.
// (In case of combined issuance/disclosure sessions, this method does not check whether or not
// the identity provider is allowed to verify the attributes being verified; use CanVerifyOrSign
// for that).
53
54
func (conf *Configuration) CanIssue(requestor string, creds []*irma.CredentialRequest) (bool, string) {
	permissions := append(conf.Requestors[requestor].Issuing, conf.GlobalPermissions.Issuing...)
Sietse Ringers's avatar
Sietse Ringers committed
55
56
57
	if len(permissions) == 0 { // requestor is not present in the permissions
		return false, ""
	}
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

	for _, cred := range creds {
		id := cred.CredentialTypeID
		if contains(permissions, "*") ||
			contains(permissions, id.Root()+".*") ||
			contains(permissions, id.IssuerIdentifier().String()+".*") ||
			contains(permissions, id.String()) {
			continue
		} else {
			return false, id.String()
		}
	}

	return true, ""
}

Sietse Ringers's avatar
Sietse Ringers committed
74
75
// CanVerifyOrSign returns whether or not the specified requestor may use the selected attributes
// in any of the supported session types.
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
func (conf *Configuration) CanVerifyOrSign(requestor string, action irma.Action, disjunctions irma.AttributeDisjunctionList) (bool, string) {
	var permissions []string
	switch action {
	case irma.ActionDisclosing:
		permissions = append(conf.Requestors[requestor].Disclosing, conf.GlobalPermissions.Disclosing...)
	case irma.ActionIssuing:
		permissions = append(conf.Requestors[requestor].Disclosing, conf.GlobalPermissions.Disclosing...)
	case irma.ActionSigning:
		permissions = append(conf.Requestors[requestor].Signing, conf.GlobalPermissions.Signing...)
	}
	if len(permissions) == 0 { // requestor is not present in the permissions
		return false, ""
	}

	for _, disjunction := range disjunctions {
		for _, attr := range disjunction.Attributes {
			if contains(permissions, "*") ||
				contains(permissions, attr.Root()+".*") ||
				contains(permissions, attr.CredentialTypeIdentifier().IssuerIdentifier().String()+".*") ||
				contains(permissions, attr.CredentialTypeIdentifier().String()+".*") ||
				contains(permissions, attr.String()) {
				continue
			} else {
				return false, attr.String()
			}
		}
	}

	return true, ""
}

func (conf *Configuration) initialize() error {
	if err := conf.readPrivateKey(); err != nil {
		return err
	}

	if !conf.AuthenticateRequestors {
Sietse Ringers's avatar
Sietse Ringers committed
113
		conf.Logger.Warn("Authentication of incoming session requests disabled")
114
		authenticators = map[AuthenticationMethod]Authenticator{AuthenticationMethodNone: NilAuthenticator{}}
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

		// Leaving the global permission whitelists empty in this mode means enabling it for everyone
		if len(conf.GlobalPermissions.Disclosing) == 0 {
			conf.Logger.Info("No disclosing whitelist found: allowing verification of any attribute")
			conf.GlobalPermissions.Disclosing = []string{"*"}
		}
		if len(conf.GlobalPermissions.Signing) == 0 {
			conf.Logger.Info("No signing whitelist found: allowing attribute-based signature sessions with any attribute")
			conf.GlobalPermissions.Signing = []string{"*"}
		}
		if len(conf.GlobalPermissions.Issuing) == 0 {
			conf.Logger.Info("No issuance whitelist found: allowing issuance of any credential (for which private keys are installed)")
			conf.GlobalPermissions.Issuing = []string{"*"}
		}
		return nil
	}

132
133
	authenticators = map[AuthenticationMethod]Authenticator{
		AuthenticationMethodPublicKey: &PublicKeyAuthenticator{publickeys: map[string]*rsa.PublicKey{}},
134
		AuthenticationMethodToken:     &PresharedKeyAuthenticator{presharedkeys: map[string]string{}},
135
136
	}

Sietse Ringers's avatar
Sietse Ringers committed
137
	// Initialize authenticators
138
139
140
141
142
143
	for name, requestor := range conf.Requestors {
		authenticator, ok := authenticators[requestor.AuthenticationMethod]
		if !ok {
			return errors.Errorf("Requestor %s has unsupported authentication type")
		}
		if err := authenticator.Initialize(name, requestor); err != nil {
144
145
146
147
148
149
150
151
			return err
		}
	}

	return nil
}

func (conf *Configuration) readPrivateKey() error {
152
	if conf.JwtPrivateKey == "" {
153
154
155
156
157
		return nil
	}

	var keybytes []byte
	var err error
158
159
	if strings.HasPrefix(conf.JwtPrivateKey, "-----BEGIN") {
		keybytes = []byte(conf.JwtPrivateKey)
160
	} else {
161
		if err = fs.AssertPathExists(conf.JwtPrivateKey); err != nil {
162
163
			return err
		}
164
		if keybytes, err = ioutil.ReadFile(conf.JwtPrivateKey); err != nil {
165
166
167
168
			return err
		}
	}

169
	conf.jwtPrivateKey, err = jwt.ParseRSAPrivateKeyFromPEM(keybytes)
170
171
	return err
}
Sietse Ringers's avatar
Sietse Ringers committed
172
173
174
175
176
177
178
179
180
181

// Return true iff query equals an element of strings.
func contains(strings []string, query string) bool {
	for _, s := range strings {
		if s == query {
			return true
		}
	}
	return false
}