api.go 4.52 KB
Newer Older
Sietse Ringers's avatar
Sietse Ringers committed
1
package server
2
3

import (
4
	"encoding/json"
5
	"errors"
6
7
8
	"net/http"
	"runtime/debug"

9
	"github.com/Sirupsen/logrus"
10
	"github.com/privacybydesign/gabi"
11
12
13
	"github.com/privacybydesign/irmago"
)

14
15
var Logger *logrus.Logger = logrus.StandardLogger()

16
// Configuration contains configuration for the irmarequestor library and irmaserver.
17
type Configuration struct {
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	// irma_configuration. If not given, this will be popupated using IrmaConfigurationPath.
	IrmaConfiguration *irma.Configuration `json:"-"`
	// Path to schemes to parse (only used if IrmaConfiguration is not given)
	IrmaConfigurationPath string `json:"irmaconf" mapstructure:"irmaconf"`
	// Path to writable dir to write cache to (only used if IrmaConfiguration is not give)
	CachePath string `json:"cachepath" mapstructure:"cachepath"`
	// Path to issuer private keys to parse
	IssuerPrivateKeysPath string `json:"privatekeys" mapstructure:"privatekeys"`
	// Issuer private keys
	IssuerPrivateKeys map[irma.IssuerIdentifier]*gabi.PrivateKey `json:"-"`
	// URL at which the IRMA app can reach this server during sessions
	URL string `json:"url" mapstructure:"url"`
	// Logging
	Logger *logrus.Logger `json:"-"`
32
33
}

34
35
// SessionResult contains session information such as the session status, type, possible errors,
// and disclosed attributes or attribute-based signature if appropriate to the session type.
36
type SessionResult struct {
Sietse Ringers's avatar
Sietse Ringers committed
37
38
	Token       string
	Status      Status
39
	Type        irma.Action
Sietse Ringers's avatar
Sietse Ringers committed
40
41
42
43
	ProofStatus irma.ProofStatus
	Disclosed   []*irma.DisclosedAttribute
	Signature   *irma.SignedMessage
	Err         *irma.RemoteError
44
45
}

Sietse Ringers's avatar
Sietse Ringers committed
46
// Status is the status of an IRMA session.
47
48
49
type Status string

const (
Sietse Ringers's avatar
Sietse Ringers committed
50
51
52
53
54
	StatusInitialized Status = "INITIALIZED" // The session has been started and is waiting for the client
	StatusConnected   Status = "CONNECTED"   // The client has retrieved the session request, we wait for its response
	StatusCancelled   Status = "CANCELLED"   // The session is cancelled, possibly due to an error
	StatusDone        Status = "DONE"        // The session has completed successfully
	StatusTimeout     Status = "TIMEOUT"     // Session timed out
55
)
56

57
// RemoteError converts an error and an explaining message to an *irma.RemoteError.
58
59
60
61
62
63
64
65
66
67
68
69
func RemoteError(err Error, message string) *irma.RemoteError {
	stack := string(debug.Stack())
	Logger.Errorf("Error: %d %s %s\n%s", err.Status, err.Type, message, stack)
	return &irma.RemoteError{
		Status:      err.Status,
		Description: err.Description,
		ErrorName:   string(err.Type),
		Message:     message,
		Stacktrace:  stack,
	}
}

70
71
// JsonResponse JSON-marshals the specified object or error
// and returns it along with a suitable HTTP status code
72
73
74
75
76
77
78
79
80
81
82
83
84
85
func JsonResponse(v interface{}, err *irma.RemoteError) (int, []byte) {
	msg := v
	status := http.StatusOK
	if err != nil {
		msg = err
		status = err.Status
	}
	b, e := json.Marshal(msg)
	if e != nil {
		Logger.Error("Failed to serialize response:", e.Error())
		return http.StatusInternalServerError, nil
	}
	return status, b
}
86

87
// WriteError writes the specified error and explaining message as JSON to the http.ResponseWriter.
88
func WriteError(w http.ResponseWriter, err Error, msg string) {
89
	WriteResponse(w, nil, RemoteError(err, msg))
90
91
}

92
// WriteJson writes the specified object as JSON to the http.ResponseWriter.
93
func WriteJson(w http.ResponseWriter, object interface{}) {
94
95
96
	WriteResponse(w, object, nil)
}

97
// WriteResponse writes the specified object or error as JSON to the http.ResponseWriter.
98
99
func WriteResponse(w http.ResponseWriter, object interface{}, rerr *irma.RemoteError) {
	status, bts := JsonResponse(object, rerr)
100
101
102
103
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	w.Write(bts)
}
104

105
// WriteString writes the specified string to the http.ResponseWriter.
106
107
108
109
110
func WriteString(w http.ResponseWriter, str string) {
	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(str))
}
111

112
113
114
// ParseSessionRequest tries to parse the specified bytes as an
// disclosure request, a signature request, and an issuance request, in that order.
// Returns an error if none of the attempts work.
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
func ParseSessionRequest(bts []byte) (request irma.SessionRequest, err error) {
	request = &irma.DisclosureRequest{}
	if err = irma.UnmarshalValidate(bts, request); err == nil {
		return request, nil
	}
	request = &irma.SignatureRequest{}
	if err = irma.UnmarshalValidate(bts, request); err == nil {
		return request, nil
	}
	request = &irma.IssuanceRequest{}
	if err = irma.UnmarshalValidate(bts, request); err == nil {
		return request, nil
	}
	return nil, errors.New("Invalid or disabled session type")
}