Commit 65876b5f authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Improve protocol version negotiation, identifier serialization, HTTP error handling

parent 808ddb58
package irmago
import "strings"
import (
"encoding/json"
"strings"
)
type metaObjectIdentifier string
......@@ -92,3 +95,11 @@ func (id AttributeTypeIdentifier) IsCredential() bool {
func (ai *AttributeIdentifier) CredentialIdentifier() CredentialIdentifier {
return CredentialIdentifier{Type: ai.Type.CredentialTypeIdentifier(), Index: ai.Index, Count: ai.Count}
}
func (id AttributeTypeIdentifier) MarshalJSON() ([]byte, error) {
return json.Marshal(id.String())
}
func (id CredentialTypeIdentifier) MarshalJSON() ([]byte, error) {
return json.Marshal(id.String())
}
......@@ -4,8 +4,13 @@ import (
"encoding/json"
"errors"
"math/big"
"strconv"
"strings"
"sort"
"fmt"
"github.com/credentials/irmago"
"github.com/mhe/gabi"
)
......@@ -42,15 +47,49 @@ type Session struct {
context *big.Int
}
// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
2: []int{2, 1},
}
func calcVersion(qr *Qr) (string, error) {
// Parse range supported by server
minmajor, err := strconv.Atoi(string(qr.ProtocolVersion[0]))
minminor, err := strconv.Atoi(string(qr.ProtocolVersion[2]))
maxmajor, err := strconv.Atoi(string(qr.ProtocolMaxVersion[0]))
maxminor, err := strconv.Atoi(string(qr.ProtocolMaxVersion[2]))
if err != nil {
return "", err
}
// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
keys := make([]int, 0, len(supportedVersions))
for k, _ := range supportedVersions {
keys = append(keys, k)
}
sort.Sort(sort.Reverse(sort.IntSlice(keys)))
for _, major := range keys {
for _, minor := range supportedVersions[major] {
aboveMinimum := major > minmajor || (major == minmajor && minor >= minminor)
underMaximum := major < maxmajor || (major == maxmajor && minor <= maxminor)
if aboveMinimum && underMaximum {
return fmt.Sprintf("%d.%d", major, minor), nil
}
}
}
return "", fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion, qr.ProtocolMaxVersion)
}
// NewSession creates and starts a new IRMA session.
func NewSession(qr *Qr, handler Handler) *Session {
if qr.ProtocolVersion != "2.1" && qr.ProtocolVersion != "2.2" { // TODO version negotiation
handler.Failure(ActionUnknown, ErrorProtocolVersionNotSupported, qr.ProtocolVersion)
version, err := calcVersion(qr)
if err != nil {
handler.Failure(ActionUnknown, ErrorProtocolVersionNotSupported, err.Error())
return nil
}
session := &Session{
Version: Version(qr.ProtocolVersion),
Version: Version(version),
Action: Action(qr.Type),
ServerURL: qr.URL,
Handler: handler,
......
......@@ -6,6 +6,8 @@ import (
"encoding/base64"
"fmt"
"github.com/credentials/irmago"
"github.com/stretchr/testify/require"
)
......@@ -44,8 +46,8 @@ func (th TestHandler) AskSignaturePermission(request SignatureRequest, ServerNam
}
func TestSession(t *testing.T) {
id := irmago.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentNumber")
url := "https://demo.irmacard.org/tomcat/irma_api_server/api/v2"
id := irmago.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
url := "https://demo.irmacard.org/tomcat/irma_api_server/api/v2/verification"
name := "testsp"
spRequest := NewServiceProviderJwt(name, DisclosureRequest{
......@@ -56,6 +58,7 @@ func TestSession(t *testing.T) {
},
}),
})
fmt.Printf("%+v\n", spRequest.Request.Request.Content[0])
headerbytes, err := json.Marshal(&map[string]string{"alg": "none", "typ": "JWT"})
require.NoError(t, err)
......@@ -63,8 +66,12 @@ func TestSession(t *testing.T) {
require.NoError(t, err)
jwt := base64.StdEncoding.EncodeToString(headerbytes) + "." + base64.StdEncoding.EncodeToString(bodybytes) + "."
qr, err := StartSession(jwt, url)
require.NoError(t, err)
fmt.Println(jwt)
qr, transportErr := StartSession(jwt, url)
if transportErr != nil {
fmt.Println(transportErr.(*TransportError).ApiErr)
}
require.NoError(t, transportErr)
NewSession(qr, TestHandler{t})
}
......@@ -15,6 +15,24 @@ type HTTPTransport struct {
client *http.Client
}
type ApiError struct {
Status int `json:"status"`
ErrorName string `json:"error"'`
Description string `json:"description"`
Message string `json:"message"`
Stacktrace string `json:"stacktrace"`
}
type TransportError struct {
Err string
Status int
ApiErr *ApiError
}
func (te TransportError) Error() string {
return te.Err
}
func NewHTTPTransport(serverURL string) *HTTPTransport {
url := serverURL
if !strings.HasSuffix(url, "/") {
......@@ -40,14 +58,14 @@ func (transport *HTTPTransport) request(url string, method string, result interf
if object != nil {
marshaled, err := json.Marshal(object)
if err != nil {
return err
return &TransportError{Err: err.Error()}
}
reader = bytes.NewBuffer(marshaled)
}
req, err := http.NewRequest(method, transport.Server+url, reader)
if err != nil {
return err
return &TransportError{Err: err.Error()}
}
req.Header.Set("User-Agent", "irmago")
......@@ -57,17 +75,25 @@ func (transport *HTTPTransport) request(url string, method string, result interf
res, err := transport.client.Do(req)
if err != nil {
return err
return &TransportError{Err: err.Error()}
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
return &TransportError{Err: err.Error(), Status: res.StatusCode}
}
if res.StatusCode != 200 {
apierr := &ApiError{}
json.Unmarshal(body, apierr)
if apierr.ErrorName == "" { // Not an ApiErrorMessage
return &TransportError{Err: err.Error(), Status: res.StatusCode}
}
return &TransportError{Err: apierr.ErrorName, Status: res.StatusCode, ApiErr: apierr}
}
err = json.Unmarshal(body, result)
if err != nil {
return err
return &TransportError{Err: err.Error(), Status: res.StatusCode}
}
return nil
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment