Commit 83219f43 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: allow non-https in irmaclient only if new developer mode is enabled

parent e3d622d5
......@@ -32,6 +32,7 @@ func TestKeyshareRegister(t *testing.T) {
require.NoError(t, client.KeyshareRemoveAll())
require.NoError(t, client.RemoveStorage())
client.SetDeveloperMode(true)
client.KeyshareEnroll(irma.NewSchemeManagerIdentifier("test"), nil, "12345", "en")
require.NoError(t, <-handler.c)
......
......@@ -50,6 +50,7 @@ func parseExistingStorage(t *testing.T, storage string) (*irmaclient.Client, *Te
handler,
)
require.NoError(t, err)
client.SetDeveloperMode(true)
return client, handler
}
......
......@@ -352,3 +352,39 @@ func TestOptionalDisclosure(t *testing.T) {
require.True(t, reflect.DeepEqual(args.disclosed, result.Disclosed))
}
}
func TestClientDeveloperMode(t *testing.T) {
client, handler := parseStorage(t)
defer test.ClearTestStorage(t, handler.storage)
StartIrmaServer(t, false)
defer StopIrmaServer()
// parseStorage returns a client with developer mode already enabled.
// Do a session with our local testserver (without https)
issuanceRequest := getNameIssuanceRequest()
requestorSessionHelper(t, issuanceRequest, client, sessionOptionReuseServer)
require.True(t, issuanceRequest.DevelopmentMode) // set to true by server
// RemoveStorage resets developer mode preference back to its default (disabled)
require.NoError(t, client.RemoveStorage())
require.False(t, client.Preferences.DeveloperMode)
// Try to start another session with our non-https server
issuanceRequest = getNameIssuanceRequest()
qr, _, err := irmaServer.StartSession(issuanceRequest, nil)
require.NoError(t, err)
c := make(chan *SessionResult, 1)
j, err := json.Marshal(qr)
require.NoError(t, err)
client.NewSession(string(j), &TestHandler{t, c, client, nil, 0, ""})
result := <-c
// Check that it failed with an appropriate error message
require.NotNil(t, result)
require.Error(t, result.Err)
serr, ok := result.Err.(*irma.SessionError)
require.True(t, ok)
require.NotNil(t, serr)
require.Equal(t, irma.ErrorInvalidRequest, serr.ErrorType)
require.Equal(t, "remote server does not use https", serr.Err.Error())
}
......@@ -120,6 +120,7 @@ func TestLargeAttribute(t *testing.T) {
defer test.ClearTestStorage(t, handler.storage)
require.NoError(t, client.RemoveStorage())
client.SetDeveloperMode(true)
issuanceRequest := getSpecialIssuanceRequest(false, "1234567890123456789012345678901234567890") // 40 chars
sessionHelper(t, issuanceRequest, "issue", client)
......@@ -202,6 +203,7 @@ func TestAttributeByteEncoding(t *testing.T) {
client, handler := parseStorage(t)
defer test.ClearTestStorage(t, handler.storage)
require.NoError(t, client.RemoveStorage())
client.SetDeveloperMode(true)
/* After bitshifting the presence bit into the large attribute below, the most significant
bit is 1. In the bigint->[]byte conversion that happens before hashing this attribute, in
......@@ -227,6 +229,7 @@ func TestOutdatedClientIrmaConfiguration(t *testing.T) {
// Remove old studentCard credential from before support for optional attributes, and issue a new one
require.NoError(t, client.RemoveStorage())
client.SetDeveloperMode(true)
require.Nil(t, requestorSessionHelper(t, getIssuanceRequest(true), client).Err)
// client does not have updated irma_configuration with new attribute irma-demo.RU.studentCard.newAttribute,
......@@ -247,6 +250,7 @@ func TestDisclosureNewAttributeUpdateSchemeManager(t *testing.T) {
// Remove old studentCard credential from before support for optional attributes, and issue a new one
require.NoError(t, client.RemoveStorage())
client.SetDeveloperMode(true)
require.Nil(t, requestorSessionHelper(t, getIssuanceRequest(true), client).Err)
// Trigger downloading the updated irma_configuration using a disclosure request containing the
......
......@@ -68,12 +68,15 @@ type Client struct {
credMutex sync.Mutex
}
// TODO: Decide if we want to remove this functionality, or keep it for future preferences
// Also consider if we should save irmamobile preferences here, because they would automatically
// TODO: consider if we should save irmamobile preferences here, because they would automatically
// be part of any backup and syncing solution we implement at a later time
type Preferences struct{}
type Preferences struct {
DeveloperMode bool
}
var defaultPreferences = Preferences{}
var defaultPreferences = Preferences{
DeveloperMode: false,
}
// KeyshareHandler is used for asking the user for his email address and PIN,
// for enrolling at a keyshare server.
......@@ -1122,9 +1125,14 @@ func (client *Client) LoadLogsBefore(beforeIndex uint64, max int) ([]*LogEntry,
return client.storage.LoadLogsBefore(beforeIndex, max)
}
// TODO: See comment at Preferences struct definition (client.go)
func (client *Client) applyPreferences() {
func (client *Client) SetDeveloperMode(enable bool) {
client.Preferences.DeveloperMode = enable
_ = client.storage.StorePreferences(client.Preferences)
client.applyPreferences()
}
func (client *Client) applyPreferences() {
irma.ForceHttps = !client.Preferences.DeveloperMode
}
// ConfigurationUpdated should be run after Configuration.Download().
......
......@@ -45,6 +45,7 @@ func parseExistingStorage(t *testing.T, storage string) (*Client, *TestClientHan
handler,
)
require.NoError(t, err)
client.SetDeveloperMode(true)
return client, handler
}
......
......@@ -114,21 +114,33 @@ func (client *Client) NewSession(sessionrequest string, handler Handler) Session
bts := []byte(sessionrequest)
qr := &irma.Qr{}
if err := irma.UnmarshalValidate(bts, qr); err == nil {
if err := json.Unmarshal(bts, qr); err == nil && qr.IsQr() {
if err = qr.Validate(); err != nil {
handler.Failure(&irma.SessionError{ErrorType: irma.ErrorInvalidRequest, Err: err})
return nil
}
return client.newQrSession(qr, handler)
}
sigRequest := &irma.SignatureRequest{}
if err := irma.UnmarshalValidate(bts, sigRequest); err == nil {
if err := json.Unmarshal(bts, sigRequest); err == nil && sigRequest.IsSignatureRequest() {
if err = sigRequest.Validate(); err != nil {
handler.Failure(&irma.SessionError{ErrorType: irma.ErrorInvalidRequest, Err: err})
return nil
}
return client.newManualSession(sigRequest, handler, irma.ActionSigning)
}
disclosureRequest := &irma.DisclosureRequest{}
if err := irma.UnmarshalValidate(bts, disclosureRequest); err == nil {
if err := json.Unmarshal(bts, disclosureRequest); err == nil && disclosureRequest.IsDisclosureRequest() {
if err = disclosureRequest.Validate(); err != nil {
handler.Failure(&irma.SessionError{ErrorType: irma.ErrorInvalidRequest, Err: err})
return nil
}
return client.newManualSession(disclosureRequest, handler, irma.ActionDisclosing)
}
handler.Failure(&irma.SessionError{Err: errors.New("Session request could not be parsed"), Info: sessionrequest})
handler.Failure(&irma.SessionError{ErrorType: irma.ErrorInvalidRequest, Info: "session request of unsupported type"})
return nil
}
......@@ -259,6 +271,13 @@ func (session *session) processSessionInfo() {
}
baserequest := session.request.Base()
if baserequest.DevelopmentMode && !session.client.Preferences.DeveloperMode {
session.fail(&irma.SessionError{
ErrorType: irma.ErrorInvalidRequest,
Info: "server running in developer mode: either switch to production mode, or enable developer mode in IRMA app",
})
return
}
confirmedProtocolVersion := baserequest.ProtocolVersion
if confirmedProtocolVersion != nil {
session.Version = confirmedProtocolVersion
......
......@@ -18,8 +18,7 @@ import (
// Status encodes the status of an IRMA session (e.g., connected).
type Status string
// disabled until we offer a convenient way to toggle this in irma_mobile
var ForceHttps bool = false
var ForceHttps = true
const (
MinVersionHeader = "X-IRMA-MinProtocolVersion"
......@@ -323,26 +322,31 @@ func ParseRequestorJwt(action string, requestorJwt string) (RequestorJwt, error)
return retval, nil
}
func (qr *Qr) IsQr() bool {
switch qr.Type {
case ActionDisclosing: // nop
case ActionIssuing: // nop
case ActionSigning: // nop
case ActionRedirect: // nop
default:
return false
}
return true
}
func (qr *Qr) Validate() (err error) {
if qr.URL == "" {
return errors.New("No URL specified")
return errors.New("no URL specified")
}
var u *url.URL
if u, err = url.ParseRequestURI(qr.URL); err != nil {
return errors.Errorf("Invalid URL: %s", err.Error())
return errors.Errorf("invalid URL: %s", err.Error())
}
if ForceHttps && u.Scheme != "https" {
return errors.Errorf("URL did not begin with https")
return errors.Errorf("remote server does not use https")
}
switch qr.Type {
case ActionDisclosing: // nop
case ActionIssuing: // nop
case ActionSigning: // nop
case ActionRedirect: // nop
default:
return errors.New("Unsupported session type")
if !qr.IsQr() {
return errors.New("unsupported session type")
}
return nil
}
......@@ -40,8 +40,9 @@ type BaseRequest struct {
ids *IrmaIdentifierSet // cache for Identifiers() method
legacy bool // Whether or not this was deserialized from a legacy (pre-condiscon) request
Type Action `json:"type,omitempty"` // Session type, only used in legacy code
legacy bool // Whether or not this was deserialized from a legacy (pre-condiscon) request
Type Action `json:"type,omitempty"` // Session type, only used in legacy code
DevelopmentMode bool `json:"devMode,omitempty"`
ClientReturnURL string `json:"clientReturnUrl,omitempty"` // URL to proceed to when IRMA session is completed
}
......@@ -537,8 +538,12 @@ func (dr *DisclosureRequest) Base() *BaseRequest {
func (dr *DisclosureRequest) Action() Action { return ActionDisclosing }
func (dr *DisclosureRequest) IsDisclosureRequest() bool {
return dr.LDContext == LDContextDisclosureRequest
}
func (dr *DisclosureRequest) Validate() error {
if dr.LDContext != LDContextDisclosureRequest {
if !dr.IsDisclosureRequest() {
return errors.New("Not a disclosure request")
}
if len(dr.Disclose) == 0 {
......@@ -746,8 +751,12 @@ func (sr *SignatureRequest) SignatureFromMessage(message interface{}, timestamp
func (sr *SignatureRequest) Action() Action { return ActionSigning }
func (sr *SignatureRequest) IsSignatureRequest() bool {
return sr.LDContext == LDContextSignatureRequest
}
func (sr *SignatureRequest) Validate() error {
if sr.LDContext != LDContextSignatureRequest {
if !sr.IsSignatureRequest() {
return errors.New("Not a signature request")
}
if sr.Message == "" {
......
......@@ -172,6 +172,7 @@ func (s *Server) StartSession(req interface{}, handler server.SessionHandler) (*
}
}
request.Base().DevelopmentMode = !s.conf.Production
session := s.newSession(action, rrequest)
s.conf.Logger.WithFields(logrus.Fields{"action": action, "session": session.token}).Infof("Session started")
if s.conf.Logger.IsLevelEnabled(logrus.DebugLevel) {
......
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