Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
IRMA
Github mirrors
irmago
Commits
fada009f
Commit
fada009f
authored
Sep 23, 2019
by
Sietse Ringers
Browse files
feat: support static QRs in irma server and irmaclient
parent
33cdda49
Changes
7
Hide whitespace changes
Inline
Side-by-side
internal/sessiontest/server_test.go
View file @
fada009f
...
...
@@ -6,6 +6,7 @@ import (
"testing"
"time"
irma
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/test"
"github.com/privacybydesign/irmago/server"
"github.com/privacybydesign/irmago/server/irmaserver"
...
...
@@ -118,5 +119,18 @@ var JwtServerConfiguration = &requestorserver.Configuration{
AuthenticationKey
:
"eGE2PSomOT84amVVdTU+LmYtJXJWZ2BmNjNwSGltCg=="
,
},
},
StaticSessions
:
map
[
string
]
interface
{}{
"staticsession"
:
irma
.
ServiceProviderRequest
{
RequestorBaseRequest
:
irma
.
RequestorBaseRequest
{
CallbackUrl
:
"http://localhost:48685"
,
},
Request
:
&
irma
.
DisclosureRequest
{
BaseRequest
:
irma
.
BaseRequest
{
LDContext
:
irma
.
LDContextDisclosureRequest
},
Disclose
:
irma
.
AttributeConDisCon
{
{{
irma
.
NewAttributeRequest
(
"irma-demo.RU.studentCard.level"
)}},
},
},
},
},
JwtPrivateKeyFile
:
filepath
.
Join
(
testdata
,
"jwtkeys"
,
"sk.pem"
),
}
internal/sessiontest/session_test.go
View file @
fada009f
package
sessiontest
import
(
"context"
"encoding/json"
"net/http"
"path/filepath"
"reflect"
"testing"
"
path/filepath
"
"
time
"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/fs"
...
...
@@ -388,3 +391,41 @@ func TestDownloadSchemeManager(t *testing.T) {
require
.
NoError
(
t
,
err
)
require
.
True
(
t
,
exists
)
}
func
TestStaticQRSession
(
t
*
testing
.
T
)
{
client
,
_
:=
parseStorage
(
t
)
defer
test
.
ClearTestStorage
(
t
)
StartRequestorServer
(
JwtServerConfiguration
)
defer
StopRequestorServer
()
// start server to receive session result callback after the session
var
received
bool
mux
:=
http
.
NewServeMux
()
mux
.
HandleFunc
(
"/"
,
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
received
=
true
})
s
:=
&
http
.
Server
{
Addr
:
":48685"
,
Handler
:
mux
}
go
func
()
{
_
=
s
.
ListenAndServe
()
}()
// setup static QR and other variables
qr
:=
&
irma
.
Qr
{
Type
:
irma
.
ActionRedirect
,
URL
:
"http://localhost:48682/session/-/static/staticsession"
,
}
bts
,
err
:=
json
.
Marshal
(
qr
)
require
.
NoError
(
t
,
err
)
localhost
:=
"localhost"
host
:=
irma
.
NewTranslatedString
(
&
localhost
)
c
:=
make
(
chan
*
SessionResult
)
// Perform session
client
.
NewSession
(
string
(
bts
),
TestHandler
{
t
,
c
,
client
,
host
})
if
result
:=
<-
c
;
result
!=
nil
{
require
.
NoError
(
t
,
result
.
Err
)
}
// give irma server time to post session result to the server started above, and check the call was received
time
.
Sleep
(
200
*
time
.
Millisecond
)
require
.
NoError
(
t
,
s
.
Shutdown
(
context
.
Background
()))
require
.
True
(
t
,
received
)
}
irmaclient/session.go
View file @
fada009f
...
...
@@ -164,6 +164,19 @@ func (client *Client) newSchemeSession(qr *irma.SchemeManagerRequest, handler Ha
// newQrSession creates and starts a new interactive IRMA session
func
(
client
*
Client
)
newQrSession
(
qr
*
irma
.
Qr
,
handler
Handler
)
SessionDismisser
{
if
qr
.
Type
==
irma
.
ActionRedirect
{
newqr
:=
&
irma
.
Qr
{}
if
err
:=
irma
.
NewHTTPTransport
(
""
)
.
Post
(
qr
.
URL
,
newqr
,
struct
{}{});
err
!=
nil
{
handler
.
Failure
(
&
irma
.
SessionError
{
ErrorType
:
irma
.
ErrorTransport
,
Err
:
errors
.
Wrap
(
err
,
0
)})
return
nil
}
if
newqr
.
Type
==
irma
.
ActionRedirect
{
// explicitly avoid infinite recursion
handler
.
Failure
(
&
irma
.
SessionError
{
ErrorType
:
irma
.
ErrorInvalidRequest
,
Err
:
errors
.
New
(
"infinite static QR recursion"
)})
return
nil
}
return
client
.
newQrSession
(
newqr
,
handler
)
}
u
,
_
:=
url
.
ParseRequestURI
(
qr
.
URL
)
// Qr validator already checked this for errors
session
:=
&
session
{
ServerURL
:
qr
.
URL
,
...
...
messages.go
View file @
fada009f
...
...
@@ -163,6 +163,7 @@ const (
ActionDisclosing
=
Action
(
"disclosing"
)
ActionSigning
=
Action
(
"signing"
)
ActionIssuing
=
Action
(
"issuing"
)
ActionRedirect
=
Action
(
"redirect"
)
ActionUnknown
=
Action
(
"unknown"
)
)
...
...
@@ -312,6 +313,7 @@ func (qr *Qr) Validate() (err error) {
case
ActionDisclosing
:
// nop
case
ActionIssuing
:
// nop
case
ActionSigning
:
// nop
case
ActionRedirect
:
// nop
default
:
return
errors
.
New
(
"Unsupported session type"
)
}
...
...
server/irmad/cmd/root.go
View file @
fada009f
...
...
@@ -125,6 +125,7 @@ func setFlags(cmd *cobra.Command, production bool) error {
issHelp
+=
" (default *)"
}
flags
.
StringSlice
(
"issue-perms"
,
nil
,
issHelp
)
flags
.
String
(
"static-sessions"
,
""
,
"preconfigured static sessions (in JSON)"
)
flags
.
Lookup
(
"no-auth"
)
.
Header
=
`Requestor authentication and default requestor permissions`
flags
.
StringP
(
"jwt-issuer"
,
"j"
,
"irmaserver"
,
"JWT issuer"
)
...
...
@@ -274,11 +275,32 @@ func configure(cmd *cobra.Command) error {
}
}
if
err
=
handleMapOrString
(
"static-sessions"
,
&
conf
.
StaticSessions
);
err
!=
nil
{
return
err
}
logger
.
Debug
(
"Done configuring"
)
return
nil
}
func
handleMapOrString
(
key
string
,
dest
interface
{})
error
{
var
m
map
[
string
]
interface
{}
var
err
error
if
val
,
flagOrEnv
:=
viper
.
Get
(
key
)
.
(
string
);
!
flagOrEnv
||
val
!=
""
{
if
m
,
err
=
cast
.
ToStringMapE
(
viper
.
Get
(
key
));
err
!=
nil
{
return
errors
.
WrapPrefix
(
err
,
"Failed to unmarshal "
+
key
+
" from flag or env var"
,
0
)
}
}
if
len
(
m
)
==
0
{
return
nil
}
if
err
:=
mapstructure
.
Decode
(
m
,
dest
);
err
!=
nil
{
return
errors
.
WrapPrefix
(
err
,
"Failed to unmarshal "
+
key
+
" from config file"
,
0
)
}
return
nil
}
func
handlePermission
(
typ
string
)
[]
string
{
if
!
viper
.
IsSet
(
typ
)
&&
(
!
viper
.
GetBool
(
"production"
)
||
typ
!=
"issue-perms"
)
{
return
[]
string
{
"*"
}
...
...
server/requestorserver/conf.go
View file @
fada009f
...
...
@@ -3,6 +3,7 @@ package requestorserver
import
(
"crypto/rsa"
"crypto/tls"
"encoding/json"
"fmt"
"regexp"
"strconv"
...
...
@@ -65,7 +66,10 @@ type Configuration struct {
// Host static files under this URL prefix
StaticPrefix
string
`json:"static_prefix" mapstructure:"static_prefix"`
jwtPrivateKey
*
rsa
.
PrivateKey
StaticSessions
map
[
string
]
interface
{}
`json:"static_sessions"`
staticSessions
map
[
string
]
irma
.
RequestorRequest
jwtPrivateKey
*
rsa
.
PrivateKey
}
// Permissions specify which attributes or credential a requestor may verify or issue.
...
...
@@ -247,6 +251,32 @@ func (conf *Configuration) initialize() error {
}
}
if
len
(
conf
.
StaticSessions
)
!=
0
&&
conf
.
jwtPrivateKey
==
nil
{
conf
.
Logger
.
Warn
(
"Static sessions enabled and no JWT private key installed. Ensure that POSTs to the callback URLs of static sessions are trustworthy by keeping the callback URLs secret and by using HTTPS."
)
}
conf
.
staticSessions
=
make
(
map
[
string
]
irma
.
RequestorRequest
)
for
name
,
r
:=
range
conf
.
StaticSessions
{
if
!
regexp
.
MustCompile
(
"^[a-zA-Z0-9_]+$"
)
.
MatchString
(
name
)
{
return
errors
.
Errorf
(
"static session name %s not allowed, must be alphanumeric"
,
name
)
}
j
,
err
:=
json
.
Marshal
(
r
)
if
err
!=
nil
{
return
errors
.
WrapPrefix
(
err
,
"failed to parse static session request "
+
name
,
0
)
}
rrequest
,
err
:=
server
.
ParseSessionRequest
(
j
)
if
err
!=
nil
{
return
errors
.
WrapPrefix
(
err
,
"failed to parse static session request "
+
name
,
0
)
}
action
:=
rrequest
.
SessionRequest
()
.
Action
()
if
action
!=
irma
.
ActionDisclosing
&&
action
!=
irma
.
ActionSigning
{
return
errors
.
Errorf
(
"static session %s must be either a disclosing or signing session"
,
name
)
}
if
rrequest
.
Base
()
.
CallbackUrl
==
""
{
return
errors
.
Errorf
(
"static session %s has no callback URL"
,
name
)
}
conf
.
staticSessions
[
name
]
=
rrequest
}
return
nil
}
...
...
server/requestorserver/server.go
View file @
fada009f
...
...
@@ -14,6 +14,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/dgrijalva/jwt-go"
...
...
@@ -163,13 +164,21 @@ var corsOptions = cors.Options{
func
(
s
*
Server
)
ClientHandler
()
http
.
Handler
{
router
:=
chi
.
NewRouter
()
router
.
Use
(
cors
.
New
(
corsOptions
)
.
Handler
)
s
.
attachClientEndpoints
(
router
)
return
router
}
func
(
s
*
Server
)
attachClientEndpoints
(
router
*
chi
.
Mux
)
{
router
.
Mount
(
"/irma/"
,
s
.
irmaserv
.
HandlerFunc
())
if
s
.
conf
.
StaticPath
!=
""
{
router
.
Mount
(
s
.
conf
.
StaticPrefix
,
s
.
StaticFilesHandler
())
}
return
router
router
.
Group
(
func
(
r
chi
.
Router
)
{
if
s
.
conf
.
Verbose
>=
2
{
r
.
Use
(
s
.
logHandler
(
"staticsession"
,
true
,
true
,
true
))
}
r
.
Post
(
"/session/-/static/{name}"
,
s
.
handleCreateStatic
)
})
}
// Handler returns a http.Handler that handles all IRMA requestor messages
...
...
@@ -180,10 +189,7 @@ func (s *Server) Handler() http.Handler {
if
!
s
.
conf
.
separateClientServer
()
{
// Mount server for irmaclient
router
.
Mount
(
"/irma/"
,
s
.
irmaserv
.
HandlerFunc
())
if
s
.
conf
.
StaticPath
!=
""
{
router
.
Mount
(
s
.
conf
.
StaticPrefix
,
s
.
StaticFilesHandler
())
}
s
.
attachClientEndpoints
(
router
)
}
router
.
NotFound
(
s
.
logHandler
(
"requestor"
,
false
,
true
,
true
)(
router
.
NotFoundHandler
())
.
ServeHTTP
)
...
...
@@ -353,6 +359,21 @@ func (s *Server) handleCreate(w http.ResponseWriter, r *http.Request) {
})
}
func
(
s
*
Server
)
handleCreateStatic
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
name
:=
chi
.
URLParam
(
r
,
"name"
)
rrequest
:=
s
.
conf
.
staticSessions
[
name
]
if
rrequest
==
nil
{
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
"unknown static session"
)
return
}
qr
,
_
,
err
:=
s
.
irmaserv
.
StartSession
(
rrequest
,
s
.
doResultCallback
)
if
err
!=
nil
{
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
return
}
server
.
WriteJson
(
w
,
qr
)
}
func
(
s
*
Server
)
handleStatus
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
res
:=
s
.
irmaserv
.
GetSessionResult
(
chi
.
URLParam
(
r
,
"token"
))
if
res
==
nil
{
...
...
@@ -527,20 +548,42 @@ func (s *Server) resultJwt(sessionresult *server.SessionResult) (string, error)
func
(
s
*
Server
)
doResultCallback
(
result
*
server
.
SessionResult
)
{
callbackUrl
:=
s
.
irmaserv
.
GetRequest
(
result
.
Token
)
.
Base
()
.
CallbackUrl
if
callbackUrl
==
""
||
s
.
conf
.
jwtPrivateKey
==
nil
{
if
callbackUrl
==
""
{
return
}
s
.
conf
.
Logger
.
WithFields
(
logrus
.
Fields
{
"session"
:
result
.
Token
,
"callbackUrl"
:
callbackUrl
})
.
Debug
(
"POSTing session result"
)
j
,
err
:=
s
.
resultJwt
(
result
)
if
err
!=
nil
{
_
=
server
.
LogError
(
errors
.
WrapPrefix
(
err
,
"Failed to create JWT for result callback"
,
0
))
return
logger
:=
s
.
conf
.
Logger
.
WithFields
(
logrus
.
Fields
{
"session"
:
result
.
Token
,
"callbackUrl"
:
callbackUrl
})
if
!
strings
.
HasPrefix
(
callbackUrl
,
"https"
)
{
if
s
.
conf
.
Production
{
logger
.
Error
(
"Not POSTing session result to callback URL without TLS: attributes would be unencrypted in transit"
)
return
}
else
{
logger
.
Warn
(
"POSTing session result to callback URL without TLS: attributes are unencrypted in traffic"
)
}
}
else
{
logger
.
Debug
(
"POSTing session result"
)
}
var
res
string
if
s
.
conf
.
jwtPrivateKey
!=
nil
{
var
err
error
res
,
err
=
s
.
resultJwt
(
result
)
if
err
!=
nil
{
_
=
server
.
LogError
(
errors
.
WrapPrefix
(
err
,
"Failed to create JWT for result callback"
,
0
))
return
}
}
else
{
bts
,
err
:=
json
.
Marshal
(
result
)
if
err
!=
nil
{
_
=
server
.
LogError
(
errors
.
WrapPrefix
(
err
,
"Failed to marshal session result for result callback"
,
0
))
return
}
res
=
string
(
bts
)
}
var
x
string
// dummy for the server's return value that we don't care about
if
err
:=
irma
.
NewHTTPTransport
(
callbackUrl
)
.
Post
(
""
,
&
x
,
j
);
err
!=
nil
{
if
err
:=
irma
.
NewHTTPTransport
(
callbackUrl
)
.
Post
(
""
,
&
x
,
res
);
err
!=
nil
{
// not our problem, log it and go on
s
.
conf
.
L
ogger
.
Warn
(
errors
.
WrapPrefix
(
err
,
"Failed to POST session result to callback URL"
,
0
))
l
ogger
.
Warn
(
errors
.
WrapPrefix
(
err
,
"Failed to POST session result to callback URL"
,
0
))
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment