logs.go 6.11 KB
Newer Older
Sietse Ringers's avatar
Sietse Ringers committed
1
2
3
4
5
6
7
8
9
10
11
package irmago

import (
	"encoding/json"
	"time"

	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
)

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

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

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

28
29
const actionRemoval = Action("removal")

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

Sietse Ringers's avatar
Sietse Ringers committed
38
39
40
	// 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
41
	switch entry.Type {
Sietse Ringers's avatar
Sietse Ringers committed
42
43
44
45
	case ActionSigning:
		entry.SignedMessage = []byte(session.jwt.(*SignatureRequestorJwt).Request.Request.Message)
		entry.SignedMessageType = session.jwt.(*SignatureRequestorJwt).Request.Request.MessageType
		fallthrough
Sietse Ringers's avatar
Sietse Ringers committed
46
	case ActionDisclosing:
Sietse Ringers's avatar
Sietse Ringers committed
47
48
49
		if prooflist, ok = response.(gabi.ProofList); !ok {
			return nil, errors.New("Response was not a ProofList")
		}
Sietse Ringers's avatar
Sietse Ringers committed
50
	case ActionIssuing:
Sietse Ringers's avatar
Sietse Ringers committed
51
52
53
		if entry.Received == nil {
			entry.Received = map[CredentialTypeIdentifier][]TranslatedString{}
		}
Sietse Ringers's avatar
Sietse Ringers committed
54
		for _, req := range session.jwt.(*IdentityProviderJwt).Request.Request.Credentials {
55
			list, err := req.AttributeList(session.client.ConfigurationStore)
Sietse Ringers's avatar
Sietse Ringers committed
56
57
58
			if err != nil {
				continue // TODO?
			}
Sietse Ringers's avatar
Sietse Ringers committed
59
60
61
62
63
64
65
			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
66
67
68
69
70
		}
	default:
		return nil, errors.New("Invalid log type")
	}

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

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

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

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

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

type jsonLogEntry struct {
	Type        Action
	Time        Timestamp
	SessionInfo *logSessionInfo
Sietse Ringers's avatar
Sietse Ringers committed
124
125
126

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

	Response json.RawMessage
Sietse Ringers's avatar
Sietse Ringers committed
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
}

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,
		SessionInfo: &SessionInfo{
			Jwt:     temp.SessionInfo.Jwt,
			Nonce:   temp.SessionInfo.Nonce,
			Context: temp.SessionInfo.Context,
			Keys:    make(map[IssuerIdentifier]int),
		},
150
		Removed:           temp.Removed,
Sietse Ringers's avatar
Sietse Ringers committed
151
152
153
154
155
		Disclosed:         temp.Disclosed,
		Received:          temp.Received,
		SignedMessage:     temp.SignedMessage,
		SignedMessageType: temp.SignedMessageType,
		rawResponse:       temp.Response,
Sietse Ringers's avatar
Sietse Ringers committed
156
157
158
159
160
161
162
163
164
	}

	// TODO remove on protocol upgrade
	for iss, count := range temp.SessionInfo.Keys {
		entry.SessionInfo.Keys[NewIssuerIdentifier(iss)] = count
	}

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

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
		}
	}

176
177
178
	var si *logSessionInfo
	if entry.SessionInfo != nil {
		si = &logSessionInfo{
Sietse Ringers's avatar
Sietse Ringers committed
179
180
181
182
			Jwt:     entry.SessionInfo.Jwt,
			Nonce:   entry.SessionInfo.Nonce,
			Context: entry.SessionInfo.Context,
			Keys:    make(map[string]int),
183
184
185
186
187
188
189
190
191
192
193
		}
		// 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,
194
		Removed:           entry.Removed,
Sietse Ringers's avatar
Sietse Ringers committed
195
196
197
198
199
200
201
202
		Disclosed:         entry.Disclosed,
		Received:          entry.Received,
		SignedMessage:     entry.SignedMessage,
		SignedMessageType: entry.SignedMessageType,
	}

	return json.Marshal(temp)
}