Commit 8f545870 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: split irma server revocation command HTTP endpoints to /revocation

parent d79a6207
...@@ -55,10 +55,10 @@ func postRevocation(request *irma.RevocationRequest, url, schemespath, authmetho ...@@ -55,10 +55,10 @@ func postRevocation(request *irma.RevocationRequest, url, schemespath, authmetho
switch authmethod { switch authmethod {
case "none": case "none":
err = transport.Post("session", nil, request) err = transport.Post("revocation", nil, request)
case "token": case "token":
transport.SetHeader("Authorization", key) transport.SetHeader("Authorization", key)
err = transport.Post("session", nil, request) err = transport.Post("revocation", nil, request)
case "hmac", "rsa": case "hmac", "rsa":
sk, jwtalg, err := configureJWTKey(authmethod, key) sk, jwtalg, err := configureJWTKey(authmethod, key)
j := irma.RevocationJwt{ j := irma.RevocationJwt{
...@@ -72,7 +72,7 @@ func postRevocation(request *irma.RevocationRequest, url, schemespath, authmetho ...@@ -72,7 +72,7 @@ func postRevocation(request *irma.RevocationRequest, url, schemespath, authmetho
if err != nil { if err != nil {
die("failed to sign JWT", err) die("failed to sign JWT", err)
} }
err = transport.Post("session", nil, jwtstr) err = transport.Post("revocation", nil, jwtstr)
default: default:
die("Invalid authentication method (must be none, token, hmac or rsa)", nil) die("Invalid authentication method (must be none, token, hmac or rsa)", nil)
} }
......
...@@ -204,7 +204,7 @@ func (s *Server) Handler() http.Handler { ...@@ -204,7 +204,7 @@ func (s *Server) Handler() http.Handler {
} }
// Server routes // Server routes
r.Post("/session", s.handleCreate) r.Post("/session", s.handleCreateSession)
r.Delete("/session/{token}", s.handleDelete) r.Delete("/session/{token}", s.handleDelete)
r.Get("/session/{token}/status", s.handleStatus) r.Get("/session/{token}/status", s.handleStatus)
r.Get("/session/{token}/statusevents", s.handleStatusEvents) r.Get("/session/{token}/statusevents", s.handleStatusEvents)
...@@ -217,6 +217,14 @@ func (s *Server) Handler() http.Handler { ...@@ -217,6 +217,14 @@ func (s *Server) Handler() http.Handler {
r.Get("/publickey", s.handlePublicKey) r.Get("/publickey", s.handlePublicKey)
}) })
router.Group(func(r chi.Router) {
r.Use(cors.New(corsOptions).Handler)
if s.conf.Verbose >= 2 {
r.Use(s.logHandler("revocation", true, true, true))
}
r.Post("/revocation", s.handleRevocation)
})
return router return router
} }
...@@ -281,7 +289,7 @@ func (s *Server) StaticFilesHandler() http.Handler { ...@@ -281,7 +289,7 @@ func (s *Server) StaticFilesHandler() http.Handler {
) )
} }
func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) { func (s *Server) handleCreateSession(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
s.conf.Logger.Error("Could not read session request HTTP POST body") s.conf.Logger.Error("Could not read session request HTTP POST body")
...@@ -295,7 +303,6 @@ func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) { ...@@ -295,7 +303,6 @@ func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) {
// 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 rrequest irma.RequestorRequest
revreq *irma.RevocationRequest
requestor string requestor string
rerr *irma.RemoteError rerr *irma.RemoteError
applies bool applies bool
...@@ -305,75 +312,12 @@ func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) { ...@@ -305,75 +312,12 @@ func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) {
if applies || rerr != nil { if applies || rerr != nil {
break break
} }
applies, revreq, requestor, rerr = authenticator.AuthenticateRevocation(r.Header, body)
if applies || rerr != nil {
break
}
}
if rerr != nil {
_ = server.LogError(rerr)
server.WriteResponse(w, nil, rerr)
return
}
if !applies {
var ctype = r.Header.Get("Content-Type")
if ctype != "application/json" && ctype != "text/plain" {
s.conf.Logger.Warnf("Session request uses unsupported Content-Type: %s", ctype)
server.WriteError(w, server.ErrorInvalidRequest, "Unsupported Content-Type: "+ctype)
return
}
s.conf.Logger.Warnf("Session request uses unknown authentication method, HTTP headers: %s, HTTP POST body: %s", server.ToJson(r.Header), string(body))
server.WriteError(w, server.ErrorInvalidRequest, "Request could not be authenticated")
return
}
if rrequest != nil {
s.handleCreateSession(w, requestor, rrequest)
} else {
s.handleRevoke(w, requestor, revreq)
}
}
func (s *Server) handleCreateSession(w http.ResponseWriter, requestor string, rrequest irma.RequestorRequest) {
// Authorize request: check if the requestor is allowed to verify or issue
// the requested attributes or credentials
request := rrequest.SessionRequest()
if request.Action() == irma.ActionIssuing {
allowed, reason := s.conf.CanIssue(requestor, request.(*irma.IssuanceRequest).Credentials)
if !allowed {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor, "id": reason}).
Warn("Requestor not authorized to issue credential; full request: ", server.ToJson(request))
server.WriteError(w, server.ErrorUnauthorized, reason)
return
}
} }
condiscon := request.Disclosure().Disclose if ok := s.checkAuth(w, r, rerr, applies, body); !ok {
if len(condiscon) > 0 {
allowed, reason := s.conf.CanVerifyOrSign(requestor, request.Action(), condiscon)
if !allowed {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor, "id": reason}).
Warn("Requestor not authorized to verify attribute; full request: ", server.ToJson(request))
server.WriteError(w, server.ErrorUnauthorized, reason)
return
}
}
if rrequest.Base().CallbackURL != "" && s.conf.jwtPrivateKey == nil {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor}).Warn("Requestor provided callbackUrl but no JWT private key is installed")
server.WriteError(w, server.ErrorUnsupported, "")
return return
} }
// Everything is authenticated and parsed, we're good to go! s.createSession(w, requestor, rrequest)
qr, token, err := s.irmaserv.StartSession(rrequest, s.doResultCallback)
if err != nil {
server.WriteError(w, server.ErrorInvalidRequest, err.Error())
return
}
server.WriteJson(w, server.SessionPackage{
SessionPtr: qr,
Token: token,
})
} }
func (s *Server) handleCreateStatic(w http.ResponseWriter, r *http.Request) { func (s *Server) handleCreateStatic(w http.ResponseWriter, r *http.Request) {
...@@ -391,18 +335,32 @@ func (s *Server) handleCreateStatic(w http.ResponseWriter, r *http.Request) { ...@@ -391,18 +335,32 @@ func (s *Server) handleCreateStatic(w http.ResponseWriter, r *http.Request) {
server.WriteJson(w, qr) server.WriteJson(w, qr)
} }
func (s *Server) handleRevoke(w http.ResponseWriter, requestor string, request *irma.RevocationRequest) { func (s *Server) handleRevocation(w http.ResponseWriter, r *http.Request) {
allowed, reason := s.conf.CanRevoke(requestor, request.CredentialType) body, err := ioutil.ReadAll(r.Body)
if !allowed { if err != nil {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor, "message": reason}). s.conf.Logger.Error("Could not read revocation request HTTP POST body")
Warn("Requestor not authorized to revoke credential; full request: ", server.ToJson(request)) _ = server.LogError(err)
server.WriteError(w, server.ErrorUnauthorized, reason) server.WriteError(w, server.ErrorInvalidRequest, err.Error())
return return
} }
if err := s.irmaserv.Revoke(request.CredentialType, request.Key); err != nil {
server.WriteError(w, server.ErrorUnknown, err.Error()) var (
revreq *irma.RevocationRequest
requestor string
rerr *irma.RemoteError
applies bool
)
for _, authenticator := range authenticators {
applies, revreq, requestor, rerr = authenticator.AuthenticateRevocation(r.Header, body)
if applies || rerr != nil {
break
}
} }
server.WriteString(w, "OK") if ok := s.checkAuth(w, r, rerr, applies, body); !ok {
return
}
s.revoke(w, requestor, revreq)
} }
func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) { func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
...@@ -613,3 +571,79 @@ func (s *Server) doResultCallback(result *server.SessionResult) { ...@@ -613,3 +571,79 @@ func (s *Server) doResultCallback(result *server.SessionResult) {
logger.Warn(errors.WrapPrefix(err, "Failed to POST session result to callback URL", 0)) logger.Warn(errors.WrapPrefix(err, "Failed to POST session result to callback URL", 0))
} }
} }
func (s *Server) createSession(w http.ResponseWriter, requestor string, rrequest irma.RequestorRequest) {
// Authorize request: check if the requestor is allowed to verify or issue
// the requested attributes or credentials
request := rrequest.SessionRequest()
if request.Action() == irma.ActionIssuing {
allowed, reason := s.conf.CanIssue(requestor, request.(*irma.IssuanceRequest).Credentials)
if !allowed {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor, "id": reason}).
Warn("Requestor not authorized to issue credential; full request: ", server.ToJson(request))
server.WriteError(w, server.ErrorUnauthorized, reason)
return
}
}
condiscon := request.Disclosure().Disclose
if len(condiscon) > 0 {
allowed, reason := s.conf.CanVerifyOrSign(requestor, request.Action(), condiscon)
if !allowed {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor, "id": reason}).
Warn("Requestor not authorized to verify attribute; full request: ", server.ToJson(request))
server.WriteError(w, server.ErrorUnauthorized, reason)
return
}
}
if rrequest.Base().CallbackURL != "" && s.conf.jwtPrivateKey == nil {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor}).Warn("Requestor provided callbackUrl but no JWT private key is installed")
server.WriteError(w, server.ErrorUnsupported, "")
return
}
// Everything is authenticated and parsed, we're good to go!
qr, token, err := s.irmaserv.StartSession(rrequest, s.doResultCallback)
if err != nil {
server.WriteError(w, server.ErrorInvalidRequest, err.Error())
return
}
server.WriteJson(w, server.SessionPackage{
SessionPtr: qr,
Token: token,
})
}
func (s *Server) revoke(w http.ResponseWriter, requestor string, request *irma.RevocationRequest) {
allowed, reason := s.conf.CanRevoke(requestor, request.CredentialType)
if !allowed {
s.conf.Logger.WithFields(logrus.Fields{"requestor": requestor, "message": reason}).
Warn("Requestor not authorized to revoke credential; full request: ", server.ToJson(request))
server.WriteError(w, server.ErrorUnauthorized, reason)
return
}
if err := s.irmaserv.Revoke(request.CredentialType, request.Key); err != nil {
server.WriteError(w, server.ErrorUnknown, err.Error())
}
server.WriteString(w, "OK")
}
func (s *Server) checkAuth(w http.ResponseWriter, r *http.Request, rerr *irma.RemoteError, applies bool, body []byte) bool {
if rerr != nil {
_ = server.LogError(rerr)
server.WriteResponse(w, nil, rerr)
return false
}
if !applies {
var ctype = r.Header.Get("Content-Type")
if ctype != "application/json" && ctype != "text/plain" {
s.conf.Logger.Warnf("Session request uses unsupported Content-Type: %s", ctype)
server.WriteError(w, server.ErrorInvalidRequest, "Unsupported Content-Type: "+ctype)
return false
}
s.conf.Logger.Warnf("Session request uses unknown authentication method, HTTP headers: %s, HTTP POST body: %s", server.ToJson(r.Header), string(body))
server.WriteError(w, server.ErrorInvalidRequest, "Request could not be authenticated")
return false
}
return true
}
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