logs.go 6.48 KB
Newer Older
1
package irmaclient
Sietse Ringers's avatar
Sietse Ringers committed
2
3
4
5
6

import (
	"encoding/json"
	"time"

7
	"github.com/credentials/irmago"
Sietse Ringers's avatar
Sietse Ringers committed
8
9
10
11
12
	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
)

type LogEntry struct {
Sietse Ringers's avatar
Sietse Ringers committed
13
	// General info
14
15
16
	Type        irmago.Action
	Time        irmago.Timestamp    // Time at which the session was completed
	SessionInfo *irmago.SessionInfo // Message that started the session
Sietse Ringers's avatar
Sietse Ringers committed
17

Sietse Ringers's avatar
Sietse Ringers committed
18
	// Session type-specific info
19
20
21
22
23
	Disclosed         map[irmago.CredentialTypeIdentifier]map[int]irmago.TranslatedString // Any session type
	Received          map[irmago.CredentialTypeIdentifier][]irmago.TranslatedString       // In case of issuance session
	Removed           map[irmago.CredentialTypeIdentifier][]irmago.TranslatedString       // In case of credential removal
	SignedMessage     []byte                                                              // In case of signature sessions
	SignedMessageType string                                                              // In case of signature sessions
Sietse Ringers's avatar
Sietse Ringers committed
24

Sietse Ringers's avatar
Sietse Ringers committed
25
26
	response    interface{}     // Our response (ProofList or IssueCommitmentMessage)
	rawResponse json.RawMessage // Unparsed []byte version of response
Sietse Ringers's avatar
Sietse Ringers committed
27
28
}

29
const actionRemoval = irmago.Action("removal")
30

Sietse Ringers's avatar
Sietse Ringers committed
31
func (session *session) createLogEntry(response interface{}) (*LogEntry, error) {
Sietse Ringers's avatar
Sietse Ringers committed
32
33
	entry := &LogEntry{
		Type:        session.Action,
34
		Time:        irmago.Timestamp(time.Now()),
Sietse Ringers's avatar
Sietse Ringers committed
35
		SessionInfo: session.info,
Sietse Ringers's avatar
Sietse Ringers committed
36
		response:    response,
Sietse Ringers's avatar
Sietse Ringers committed
37
38
	}

Sietse Ringers's avatar
Sietse Ringers committed
39
40
41
	// Populate session type-specific fields of the log entry (except for .Disclosed which is handled below)
	var prooflist gabi.ProofList
	var ok bool
Sietse Ringers's avatar
Sietse Ringers committed
42
	switch entry.Type {
43
44
45
	case irmago.ActionSigning:
		entry.SignedMessage = []byte(session.jwt.(*irmago.SignatureRequestorJwt).Request.Request.Message)
		entry.SignedMessageType = session.jwt.(*irmago.SignatureRequestorJwt).Request.Request.MessageType
Sietse Ringers's avatar
Sietse Ringers committed
46
		fallthrough
47
	case irmago.ActionDisclosing:
Sietse Ringers's avatar
Sietse Ringers committed
48
49
50
		if prooflist, ok = response.(gabi.ProofList); !ok {
			return nil, errors.New("Response was not a ProofList")
		}
51
	case irmago.ActionIssuing:
Sietse Ringers's avatar
Sietse Ringers committed
52
		if entry.Received == nil {
53
			entry.Received = map[irmago.CredentialTypeIdentifier][]irmago.TranslatedString{}
Sietse Ringers's avatar
Sietse Ringers committed
54
		}
55
		for _, req := range session.jwt.(*irmago.IdentityProviderJwt).Request.Request.Credentials {
56
			list, err := req.AttributeList(session.client.ConfigurationStore)
Sietse Ringers's avatar
Sietse Ringers committed
57
58
59
			if err != nil {
				continue // TODO?
			}
Sietse Ringers's avatar
Sietse Ringers committed
60
61
62
63
64
65
66
			entry.Received[list.CredentialType().Identifier()] = list.Strings()
		}
		var msg *gabi.IssueCommitmentMessage
		if msg, ok = response.(*gabi.IssueCommitmentMessage); ok {
			prooflist = msg.Proofs
		} else {
			return nil, errors.New("Response was not a *IssueCommitmentMessage")
Sietse Ringers's avatar
Sietse Ringers committed
67
68
69
70
71
		}
	default:
		return nil, errors.New("Invalid log type")
	}

Sietse Ringers's avatar
Sietse Ringers committed
72
73
74
75
	// Populate the list of disclosed attributes .Disclosed
	for _, proof := range prooflist {
		if proofd, isproofd := proof.(*gabi.ProofD); isproofd {
			if entry.Disclosed == nil {
76
				entry.Disclosed = map[irmago.CredentialTypeIdentifier]map[int]irmago.TranslatedString{}
Sietse Ringers's avatar
Sietse Ringers committed
77
			}
78
			meta := irmago.MetadataFromInt(proofd.ADisclosed[1], session.client.ConfigurationStore)
Sietse Ringers's avatar
Sietse Ringers committed
79
			id := meta.CredentialType().Identifier()
80
			entry.Disclosed[id] = map[int]irmago.TranslatedString{}
Sietse Ringers's avatar
Sietse Ringers committed
81
82
83
84
85
			for i, attr := range proofd.ADisclosed {
				if i == 1 {
					continue
				}
				val := string(attr.Bytes())
86
				entry.Disclosed[id][i] = irmago.TranslatedString{"en": val, "nl": val}
Sietse Ringers's avatar
Sietse Ringers committed
87
88
89
90
			}
		}
	}

Sietse Ringers's avatar
Sietse Ringers committed
91
92
93
	return entry, nil
}

94
95
func (entry *LogEntry) Jwt() (irmago.RequestorJwt, error) {
	return irmago.ParseRequestorJwt(entry.Type, entry.SessionInfo.Jwt)
Sietse Ringers's avatar
Sietse Ringers committed
96
97
98
}

func (entry *LogEntry) GetResponse() (interface{}, error) {
Sietse Ringers's avatar
Sietse Ringers committed
99
	if entry.response == nil {
Sietse Ringers's avatar
Sietse Ringers committed
100
		switch entry.Type {
101
		case actionRemoval:
Sietse Ringers's avatar
Sietse Ringers committed
102
			return nil, nil
103
		case irmago.ActionSigning:
Sietse Ringers's avatar
Sietse Ringers committed
104
			fallthrough
105
		case irmago.ActionDisclosing:
Sietse Ringers's avatar
Sietse Ringers committed
106
			entry.response = []*gabi.ProofD{}
107
		case irmago.ActionIssuing:
Sietse Ringers's avatar
Sietse Ringers committed
108
			entry.response = &gabi.IssueCommitmentMessage{}
Sietse Ringers's avatar
Sietse Ringers committed
109
110
111
		default:
			return nil, errors.New("Invalid log type")
		}
Sietse Ringers's avatar
Sietse Ringers committed
112
		err := json.Unmarshal(entry.rawResponse, entry.response)
Sietse Ringers's avatar
Sietse Ringers committed
113
114
115
116
117
		if err != nil {
			return nil, err
		}
	}

Sietse Ringers's avatar
Sietse Ringers committed
118
	return entry.response, nil
Sietse Ringers's avatar
Sietse Ringers committed
119
120
121
}

type jsonLogEntry struct {
122
123
	Type        irmago.Action
	Time        irmago.Timestamp
Sietse Ringers's avatar
Sietse Ringers committed
124
	SessionInfo *logSessionInfo
Sietse Ringers's avatar
Sietse Ringers committed
125

126
127
128
129
130
	Disclosed         map[irmago.CredentialTypeIdentifier]map[int]irmago.TranslatedString `json:",omitempty"`
	Received          map[irmago.CredentialTypeIdentifier][]irmago.TranslatedString       `json:",omitempty"`
	Removed           map[irmago.CredentialTypeIdentifier][]irmago.TranslatedString       `json:",omitempty"`
	SignedMessage     []byte                                                              `json:",omitempty"`
	SignedMessageType string                                                              `json:",omitempty"`
Sietse Ringers's avatar
Sietse Ringers committed
131
132

	Response json.RawMessage
Sietse Ringers's avatar
Sietse Ringers committed
133
134
135
136
137
138
139
140
141
142
143
144
}

func (entry *LogEntry) UnmarshalJSON(bytes []byte) error {
	var err error
	temp := &jsonLogEntry{}
	if err = json.Unmarshal(bytes, temp); err != nil {
		return err
	}

	*entry = LogEntry{
		Type: temp.Type,
		Time: temp.Time,
145
		SessionInfo: &irmago.SessionInfo{
Sietse Ringers's avatar
Sietse Ringers committed
146
147
148
			Jwt:     temp.SessionInfo.Jwt,
			Nonce:   temp.SessionInfo.Nonce,
			Context: temp.SessionInfo.Context,
149
			Keys:    make(map[irmago.IssuerIdentifier]int),
Sietse Ringers's avatar
Sietse Ringers committed
150
		},
151
		Removed:           temp.Removed,
Sietse Ringers's avatar
Sietse Ringers committed
152
153
154
155
156
		Disclosed:         temp.Disclosed,
		Received:          temp.Received,
		SignedMessage:     temp.SignedMessage,
		SignedMessageType: temp.SignedMessageType,
		rawResponse:       temp.Response,
Sietse Ringers's avatar
Sietse Ringers committed
157
158
159
160
	}

	// TODO remove on protocol upgrade
	for iss, count := range temp.SessionInfo.Keys {
161
		entry.SessionInfo.Keys[irmago.NewIssuerIdentifier(iss)] = count
Sietse Ringers's avatar
Sietse Ringers committed
162
163
164
165
	}

	return nil
}
Sietse Ringers's avatar
Sietse Ringers committed
166
167
168
169
170
171
172
173
174
175
176

func (entry *LogEntry) MarshalJSON() ([]byte, error) {
	// If the entry was created using createLogEntry(), then entry.rawResponse == nil
	if len(entry.rawResponse) == 0 && entry.response != nil {
		if bytes, err := json.Marshal(entry.response); err == nil {
			entry.rawResponse = json.RawMessage(bytes)
		} else {
			return nil, err
		}
	}

177
178
179
	var si *logSessionInfo
	if entry.SessionInfo != nil {
		si = &logSessionInfo{
Sietse Ringers's avatar
Sietse Ringers committed
180
181
182
183
			Jwt:     entry.SessionInfo.Jwt,
			Nonce:   entry.SessionInfo.Nonce,
			Context: entry.SessionInfo.Context,
			Keys:    make(map[string]int),
184
185
186
187
188
189
190
191
192
193
194
		}
		// TODO remove on protocol upgrade
		for iss, count := range entry.SessionInfo.Keys {
			si.Keys[iss.String()] = count
		}
	}
	temp := &jsonLogEntry{
		Type:              entry.Type,
		Time:              entry.Time,
		Response:          entry.rawResponse,
		SessionInfo:       si,
195
		Removed:           entry.Removed,
Sietse Ringers's avatar
Sietse Ringers committed
196
197
198
199
200
201
202
203
		Disclosed:         entry.Disclosed,
		Received:          entry.Received,
		SignedMessage:     entry.SignedMessage,
		SignedMessageType: entry.SignedMessageType,
	}

	return json.Marshal(temp)
}