Commit 1131c29d authored by Sietse Ringers's avatar Sietse Ringers
Browse files

IRMA server sessions are now started with instances of irma.RequestorRequest

That is, irma.ServiceProviderRequest, irma.SignatureRequestorRequest, or irma.IdentityProviderRequest.
These structs contain new fields with which the requestor can configure the session
(they have in fact always been present in session request JWTs). Functions and endpoints
that start an IRMA session now support one of these; or an instance of irma.SessionRequest;
or JSON-serialized versions of any of those.
parent cb1c0ea9
...@@ -120,24 +120,51 @@ func WriteString(w http.ResponseWriter, str string) { ...@@ -120,24 +120,51 @@ func WriteString(w http.ResponseWriter, str string) {
w.Write([]byte(str)) w.Write([]byte(str))
} }
// ParseSessionRequest tries to parse the specified bytes as an func ParseSessionRequest(request interface{}) (irma.RequestorRequest, error) {
// disclosure request, a signature request, and an issuance request, in that order. switch r := request.(type) {
// Returns an error if none of the attempts work. case irma.RequestorRequest:
func ParseSessionRequest(bts []byte) (request irma.SessionRequest, err error) { return r, nil
request = &irma.DisclosureRequest{} case irma.SessionRequest:
if err = irma.UnmarshalValidate(bts, request); err == nil { return wrapSessionRequest(r)
return request, nil case string:
return ParseSessionRequest([]byte(r))
case []byte:
var attempts = []irma.Validator{&irma.ServiceProviderRequest{}, &irma.SignatureRequestorRequest{}, &irma.IdentityProviderRequest{}}
t, err := tryUnmarshalJson(r, attempts)
if err == nil {
return t.(irma.RequestorRequest), nil
}
attempts = []irma.Validator{&irma.DisclosureRequest{}, &irma.SignatureRequest{}, &irma.IssuanceRequest{}}
t, err = tryUnmarshalJson(r, attempts)
if err == nil {
return wrapSessionRequest(t.(irma.SessionRequest))
}
return nil, errors.New("Failed to JSON unmarshal request bytes")
default:
return nil, errors.New("Invalid request type")
} }
request = &irma.SignatureRequest{} }
if err = irma.UnmarshalValidate(bts, request); err == nil {
return request, nil func wrapSessionRequest(request irma.SessionRequest) (irma.RequestorRequest, error) {
switch r := request.(type) {
case *irma.DisclosureRequest:
return &irma.ServiceProviderRequest{Request: r}, nil
case *irma.SignatureRequest:
return &irma.SignatureRequestorRequest{Request: r}, nil
case *irma.IssuanceRequest:
return &irma.IdentityProviderRequest{Request: r}, nil
default:
return nil, errors.New("Invalid session type")
} }
request = &irma.IssuanceRequest{} }
if err = irma.UnmarshalValidate(bts, request); err == nil {
return request, nil func tryUnmarshalJson(bts []byte, attempts []irma.Validator) (irma.Validator, error) {
for _, a := range attempts {
if err := irma.UnmarshalValidate(bts, a); err == nil {
return a, nil
}
} }
Logger.Warn("Failed to parse as session request: ", string(bts)) return nil, errors.New("")
return nil, errors.New("Invalid or disabled session type")
} }
func LocalIP() (string, error) { func LocalIP() (string, error) {
......
...@@ -104,31 +104,26 @@ func Initialize(configuration *server.Configuration) error { ...@@ -104,31 +104,26 @@ func Initialize(configuration *server.Configuration) error {
return nil return nil
} }
func StartSession(request irma.SessionRequest) (*irma.Qr, string, error) { func StartSession(req interface{}) (*irma.Qr, string, error) {
if err := request.Validate(); err != nil { rrequest, err := server.ParseSessionRequest(req)
if err != nil {
return nil, "", server.LogError(err) return nil, "", server.LogError(err)
} }
action := irma.ActionUnknown
switch request.(type) { request := rrequest.SessionRequest()
case *irma.DisclosureRequest: action := request.Action()
action = irma.ActionDisclosing if action == irma.ActionIssuing {
case *irma.SignatureRequest:
action = irma.ActionSigning
case *irma.IssuanceRequest:
action = irma.ActionIssuing
if err := validateIssuanceRequest(request.(*irma.IssuanceRequest)); err != nil { if err := validateIssuanceRequest(request.(*irma.IssuanceRequest)); err != nil {
return nil, "", server.LogError(err) return nil, "", server.LogError(err)
} }
default:
return nil, "", server.LogError(errors.New("Invalid session type"))
} }
session := newSession(action, request) session := newSession(action, rrequest)
conf.Logger.Infof("%s session started, token %s", action, session.token) conf.Logger.Infof("%s session started, token %s", action, session.token)
if conf.Logger.IsLevelEnabled(logrus.DebugLevel) { if conf.Logger.IsLevelEnabled(logrus.DebugLevel) {
conf.Logger.Debug("Session request: ", server.ToJson(request)) conf.Logger.Debug("Session request: ", server.ToJson(rrequest))
} else { } else {
logPurgedRequest(request) logPurgedRequest(rrequest)
} }
return &irma.Qr{ return &irma.Qr{
Type: action, Type: action,
...@@ -145,6 +140,15 @@ func GetSessionResult(token string) *server.SessionResult { ...@@ -145,6 +140,15 @@ func GetSessionResult(token string) *server.SessionResult {
return session.result return session.result
} }
func GetRequest(token string) irma.RequestorRequest {
session := sessions.get(token)
if session == nil {
conf.Logger.Warn("Session request requested of unknown session ", token)
return nil
}
return session.rrequest
}
func CancelSession(token string) error { func CancelSession(token string) error {
session := sessions.get(token) session := sessions.get(token)
if session == nil { if session == nil {
......
...@@ -130,26 +130,25 @@ func chooseProtocolVersion(min, max *irma.ProtocolVersion) (*irma.ProtocolVersio ...@@ -130,26 +130,25 @@ func chooseProtocolVersion(min, max *irma.ProtocolVersion) (*irma.ProtocolVersio
} }
// logPurgedRequest logs the request excluding any attribute values. // logPurgedRequest logs the request excluding any attribute values.
func logPurgedRequest(request irma.SessionRequest) { func logPurgedRequest(request irma.RequestorRequest) {
// We want to log as much as possible of the request, but no attribute values. // We want to log as much as possible of the request, but no attribute values.
// We cannot just remove them from the request parameter as that would break the calling code. // We cannot just remove them from the request parameter as that would break the calling code.
// So we create a deep copy of the request from which we can then safely remove whatever we want to. // So we create a deep copy of the request from which we can then safely remove whatever we want to.
// Ugly hack alert: the easiest way to do this seems to be to convert it to JSON and then back. // Ugly hack alert: the easiest way to do this seems to be to convert it to JSON and then back.
// As we do not know the precise type of request (may be *irma.DisclosureRequest, // As we do not know the precise type of request, we use reflection to create a new instance
// *irma.SignatureRequest, or *irma.IssuanceRequest), we use reflection to create a new instance
// of the same type as request, into which we then unmarshal our copy. // of the same type as request, into which we then unmarshal our copy.
cpy := reflect.New(reflect.TypeOf(request).Elem()).Interface() cpy := reflect.New(reflect.TypeOf(request).Elem()).Interface()
bts, _ := json.Marshal(request) bts, _ := json.Marshal(request)
_ = json.Unmarshal(bts, cpy) _ = json.Unmarshal(bts, cpy)
// Remove required attribute values from any attributes to be disclosed // Remove required attribute values from any attributes to be disclosed
attrs := cpy.(irma.SessionRequest).ToDisclose() attrs := cpy.(irma.RequestorRequest).SessionRequest().ToDisclose()
for _, disjunction := range attrs { for _, disjunction := range attrs {
disjunction.Values = nil disjunction.Values = nil
} }
// Remove attribute values from attributes to be issued // Remove attribute values from attributes to be issued
if isreq, ok := cpy.(*irma.IssuanceRequest); ok { if isreq, ok := cpy.(*irma.IdentityProviderRequest); ok {
for _, cred := range isreq.Credentials { for _, cred := range isreq.Request.Credentials {
cred.Attributes = nil cred.Attributes = nil
} }
} }
......
...@@ -14,10 +14,11 @@ import ( ...@@ -14,10 +14,11 @@ import (
type session struct { type session struct {
sync.Mutex sync.Mutex
action irma.Action action irma.Action
token string token string
version *irma.ProtocolVersion version *irma.ProtocolVersion
request irma.SessionRequest rrequest irma.RequestorRequest
request irma.SessionRequest
status server.Status status server.Status
lastActive time.Time lastActive time.Time
...@@ -112,11 +113,12 @@ func (s memorySessionStore) deleteExpired() { ...@@ -112,11 +113,12 @@ func (s memorySessionStore) deleteExpired() {
var one *big.Int = big.NewInt(1) var one *big.Int = big.NewInt(1)
func newSession(action irma.Action, request irma.SessionRequest) *session { func newSession(action irma.Action, request irma.RequestorRequest) *session {
token := newSessionToken() token := newSessionToken()
s := &session{ s := &session{
action: action, action: action,
request: request, rrequest: request,
request: request.SessionRequest(),
lastActive: time.Now(), lastActive: time.Now(),
token: token, token: token,
result: &server.SessionResult{ result: &server.SessionResult{
...@@ -126,8 +128,8 @@ func newSession(action irma.Action, request irma.SessionRequest) *session { ...@@ -126,8 +128,8 @@ func newSession(action irma.Action, request irma.SessionRequest) *session {
} }
s.setStatus(server.StatusInitialized) s.setStatus(server.StatusInitialized)
nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk) nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
request.SetNonce(nonce) s.request.SetNonce(nonce)
request.SetContext(one) s.request.SetContext(one)
sessions.add(token, s) sessions.add(token, s)
return s return s
} }
......
...@@ -28,7 +28,7 @@ func Initialize(configuration *server.Configuration) error { ...@@ -28,7 +28,7 @@ func Initialize(configuration *server.Configuration) error {
// StartSession starts an IRMA session, running the handler on completion, if specified. // StartSession starts an IRMA session, running the handler on completion, if specified.
// The session token (the second return parameter) can be used in GetSessionResult() // The session token (the second return parameter) can be used in GetSessionResult()
// and CancelSession(). // and CancelSession().
func StartSession(request irma.SessionRequest, handler SessionHandler) (*irma.Qr, string, error) { func StartSession(request interface{}, handler SessionHandler) (*irma.Qr, string, error) {
qr, token, err := core.StartSession(request) qr, token, err := core.StartSession(request)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
...@@ -44,6 +44,10 @@ func GetSessionResult(token string) *server.SessionResult { ...@@ -44,6 +44,10 @@ func GetSessionResult(token string) *server.SessionResult {
return core.GetSessionResult(token) return core.GetSessionResult(token)
} }
func GetRequest(token string) irma.RequestorRequest {
return core.GetRequest(token)
}
// CancelSession cancels the specified IRMA session. // CancelSession cancels the specified IRMA session.
func CancelSession(token string) error { func CancelSession(token string) error {
return core.CancelSession(token) return core.CancelSession(token)
......
...@@ -27,7 +27,7 @@ type Authenticator interface { ...@@ -27,7 +27,7 @@ type Authenticator interface {
// it was not able to successfully authenticate the request). // it was not able to successfully authenticate the request).
Authenticate( Authenticate(
headers http.Header, body []byte, headers http.Header, body []byte,
) (applies bool, request irma.SessionRequest, requestor string, err *irma.RemoteError) ) (applies bool, request irma.RequestorRequest, requestor string, err *irma.RemoteError)
} }
type AuthenticationMethod string type AuthenticationMethod string
...@@ -55,7 +55,7 @@ var authenticators map[AuthenticationMethod]Authenticator ...@@ -55,7 +55,7 @@ var authenticators map[AuthenticationMethod]Authenticator
func (NilAuthenticator) Authenticate( func (NilAuthenticator) Authenticate(
headers http.Header, body []byte, headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) { ) (bool, irma.RequestorRequest, string, *irma.RemoteError) {
if headers.Get("Authentication") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") { if headers.Get("Authentication") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") {
return false, nil, "", nil return false, nil, "", nil
} }
...@@ -72,7 +72,7 @@ func (NilAuthenticator) Initialize(name string, requestor Requestor) error { ...@@ -72,7 +72,7 @@ func (NilAuthenticator) Initialize(name string, requestor Requestor) error {
func (hauth *HmacAuthenticator) Authenticate( func (hauth *HmacAuthenticator) Authenticate(
headers http.Header, body []byte, headers http.Header, body []byte,
) (applies bool, request irma.SessionRequest, requestor string, err *irma.RemoteError) { ) (applies bool, request irma.RequestorRequest, requestor string, err *irma.RemoteError) {
return jwtAuthenticate(headers, body, jwt.SigningMethodHS256.Name, hauth.hmackeys) return jwtAuthenticate(headers, body, jwt.SigningMethodHS256.Name, hauth.hmackeys)
} }
...@@ -100,7 +100,7 @@ func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) err ...@@ -100,7 +100,7 @@ func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) err
func (pkauth *PublicKeyAuthenticator) Authenticate( func (pkauth *PublicKeyAuthenticator) Authenticate(
headers http.Header, body []byte, headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) { ) (bool, irma.RequestorRequest, string, *irma.RemoteError) {
return jwtAuthenticate(headers, body, jwt.SigningMethodRS256.Name, pkauth.publickeys) return jwtAuthenticate(headers, body, jwt.SigningMethodRS256.Name, pkauth.publickeys)
} }
...@@ -123,7 +123,7 @@ func (pkauth *PublicKeyAuthenticator) Initialize(name string, requestor Requesto ...@@ -123,7 +123,7 @@ func (pkauth *PublicKeyAuthenticator) Initialize(name string, requestor Requesto
func (pskauth *PresharedKeyAuthenticator) Authenticate( func (pskauth *PresharedKeyAuthenticator) Authenticate(
headers http.Header, body []byte, headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) { ) (bool, irma.RequestorRequest, string, *irma.RemoteError) {
auth := headers.Get("Authentication") auth := headers.Get("Authentication")
if auth == "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") { if auth == "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") {
return false, nil, "", nil return false, nil, "", nil
...@@ -176,7 +176,7 @@ func jwtKeyExtractor(publickeys map[string]interface{}) func(token *jwt.Token) ( ...@@ -176,7 +176,7 @@ func jwtKeyExtractor(publickeys map[string]interface{}) func(token *jwt.Token) (
// jwtAuthenticate is a helper function for JWT-based authenticators that verifies and parses JWTs. // jwtAuthenticate is a helper function for JWT-based authenticators that verifies and parses JWTs.
func jwtAuthenticate( func jwtAuthenticate(
headers http.Header, body []byte, signatureAlg string, keys map[string]interface{}, headers http.Header, body []byte, signatureAlg string, keys map[string]interface{},
) (bool, irma.SessionRequest, string, *irma.RemoteError) { ) (bool, irma.RequestorRequest, string, *irma.RemoteError) {
// Read JWT and check its type // Read JWT and check its type
if headers.Get("Authorization") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "text/plain") { if headers.Get("Authorization") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "text/plain") {
return false, nil, "", nil return false, nil, "", nil
...@@ -217,7 +217,7 @@ func jwtAuthenticate( ...@@ -217,7 +217,7 @@ func jwtAuthenticate(
} }
requestor := claims.Issuer // presence is ensured by jwtKeyExtractor requestor := claims.Issuer // presence is ensured by jwtKeyExtractor
return true, parsedJwt.SessionRequest(), requestor, nil return true, parsedJwt.RequestorRequest(), requestor, nil
} }
func jwtSignatureAlg(j string) (string, error) { func jwtSignatureAlg(j string) (string, error) {
......
...@@ -138,13 +138,14 @@ func handleCreate(w http.ResponseWriter, r *http.Request) { ...@@ -138,13 +138,14 @@ func handleCreate(w http.ResponseWriter, r *http.Request) {
// We do this by feeding the HTTP POST details to all known authenticators, and see if // We do this by feeding the HTTP POST details to all known authenticators, and see if
// one of them is applicable and able to authenticate the request. // one of them is applicable and able to authenticate the request.
var ( var (
rrequest irma.RequestorRequest
request irma.SessionRequest request irma.SessionRequest
requestor string requestor string
rerr *irma.RemoteError rerr *irma.RemoteError
applies bool applies bool
) )
for _, authenticator := range authenticators { for _, authenticator := range authenticators {
applies, request, requestor, rerr = authenticator.Authenticate(r.Header, body) applies, rrequest, requestor, rerr = authenticator.Authenticate(r.Header, body)
if applies || rerr != nil { if applies || rerr != nil {
break break
} }
...@@ -163,6 +164,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request) { ...@@ -163,6 +164,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request) {
// Authorize request: check if the requestor is allowed to verify or issue // Authorize request: check if the requestor is allowed to verify or issue
// the requested attributes or credentials // the requested attributes or credentials
request = rrequest.SessionRequest()
if request.Action() == irma.ActionIssuing { if request.Action() == irma.ActionIssuing {
allowed, reason := conf.CanIssue(requestor, request.(*irma.IssuanceRequest).Credentials) allowed, reason := conf.CanIssue(requestor, request.(*irma.IssuanceRequest).Credentials)
if !allowed { if !allowed {
...@@ -184,7 +186,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request) { ...@@ -184,7 +186,7 @@ func handleCreate(w http.ResponseWriter, r *http.Request) {
} }
// Everything is authenticated and parsed, we're good to go! // Everything is authenticated and parsed, we're good to go!
qr, _, err := irmarequestor.StartSession(request, nil) qr, _, err := irmarequestor.StartSession(rrequest, nil)
if err != nil { if err != nil {
server.WriteError(w, server.ErrorInvalidRequest, err.Error()) server.WriteError(w, server.ErrorInvalidRequest, err.Error())
return return
......
Supports Markdown
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