Commit 85136337 authored by Leon's avatar Leon Committed by Sietse Ringers

More specific server responses

The server now responds with a "required attribute missing" error in case
a non-optional attribute is missing from the request, after trying
to redownload the configuration to see if it was tagged optional in the
meantime. This change required to extend the IrmaIdentifierSet with an
extra field that tracks missing AttributeTypeIdentifiers. Code that
populates this struct is also updated accordingly for possible future
consumers.
parent 8c66ff79
...@@ -40,12 +40,13 @@ type AttributeIdentifier struct { ...@@ -40,12 +40,13 @@ type AttributeIdentifier struct {
} }
// IrmaIdentifierSet contains a set (ensured by using map[...]struct{}) of all scheme managers, // IrmaIdentifierSet contains a set (ensured by using map[...]struct{}) of all scheme managers,
// all issuers, all credential types and all public keys that are involved in an IRMA session. // all issuers, all credential types, all public keys and all attribute types that are involved in an IRMA session.
type IrmaIdentifierSet struct { type IrmaIdentifierSet struct {
SchemeManagers map[SchemeManagerIdentifier]struct{} SchemeManagers map[SchemeManagerIdentifier]struct{}
Issuers map[IssuerIdentifier]struct{} Issuers map[IssuerIdentifier]struct{}
CredentialTypes map[CredentialTypeIdentifier]struct{} CredentialTypes map[CredentialTypeIdentifier]struct{}
PublicKeys map[IssuerIdentifier][]int PublicKeys map[IssuerIdentifier][]int
AttributeTypes map[AttributeTypeIdentifier]struct{}
} }
func newIrmaIdentifierSet() *IrmaIdentifierSet { func newIrmaIdentifierSet() *IrmaIdentifierSet {
...@@ -54,6 +55,7 @@ func newIrmaIdentifierSet() *IrmaIdentifierSet { ...@@ -54,6 +55,7 @@ func newIrmaIdentifierSet() *IrmaIdentifierSet {
Issuers: map[IssuerIdentifier]struct{}{}, Issuers: map[IssuerIdentifier]struct{}{},
CredentialTypes: map[CredentialTypeIdentifier]struct{}{}, CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
PublicKeys: map[IssuerIdentifier][]int{}, PublicKeys: map[IssuerIdentifier][]int{},
AttributeTypes: map[AttributeTypeIdentifier]struct{}{},
} }
} }
...@@ -194,6 +196,9 @@ func (set *IrmaIdentifierSet) join(other *IrmaIdentifierSet) { ...@@ -194,6 +196,9 @@ func (set *IrmaIdentifierSet) join(other *IrmaIdentifierSet) {
for ct := range other.CredentialTypes { for ct := range other.CredentialTypes {
set.CredentialTypes[ct] = struct{}{} set.CredentialTypes[ct] = struct{}{}
} }
for at := range other.AttributeTypes {
set.AttributeTypes[at] = struct{}{}
}
for issuer := range other.PublicKeys { for issuer := range other.PublicKeys {
if len(set.PublicKeys[issuer]) == 0 { if len(set.PublicKeys[issuer]) == 0 {
set.PublicKeys[issuer] = make([]int, 0, len(other.PublicKeys[issuer])) set.PublicKeys[issuer] = make([]int, 0, len(other.PublicKeys[issuer]))
...@@ -227,6 +232,9 @@ func (set *IrmaIdentifierSet) allSchemes() map[SchemeManagerIdentifier]struct{} ...@@ -227,6 +232,9 @@ func (set *IrmaIdentifierSet) allSchemes() map[SchemeManagerIdentifier]struct{}
for c := range set.CredentialTypes { for c := range set.CredentialTypes {
schemes[c.IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{} schemes[c.IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
} }
for a := range set.AttributeTypes {
schemes[a.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
}
return schemes return schemes
} }
...@@ -246,6 +254,9 @@ func (set *IrmaIdentifierSet) String() string { ...@@ -246,6 +254,9 @@ func (set *IrmaIdentifierSet) String() string {
for c := range set.CredentialTypes { for c := range set.CredentialTypes {
builder.WriteString(c.String() + ", ") builder.WriteString(c.String() + ", ")
} }
for a := range set.AttributeTypes {
builder.WriteString(a.String() + ", ")
}
s := builder.String() s := builder.String()
if len(s) > 0 { // strip trailing comma if len(s) > 0 { // strip trailing comma
s = s[:len(s)-2] s = s[:len(s)-2]
...@@ -254,5 +265,5 @@ func (set *IrmaIdentifierSet) String() string { ...@@ -254,5 +265,5 @@ func (set *IrmaIdentifierSet) String() string {
} }
func (set *IrmaIdentifierSet) Empty() bool { func (set *IrmaIdentifierSet) Empty() bool {
return len(set.SchemeManagers) == 0 && len(set.Issuers) == 0 && len(set.CredentialTypes) == 0 && len(set.PublicKeys) == 0 return len(set.SchemeManagers) == 0 && len(set.Issuers) == 0 && len(set.CredentialTypes) == 0 && len(set.PublicKeys) == 0 && len(set.AttributeTypes) == 0
} }
...@@ -355,6 +355,7 @@ func TestDisclosureNonexistingCredTypeUpdateSchemeManager(t *testing.T) { ...@@ -355,6 +355,7 @@ func TestDisclosureNonexistingCredTypeUpdateSchemeManager(t *testing.T) {
irma.NewCredentialTypeIdentifier("irma-demo.RU.foo"): struct{}{}, irma.NewCredentialTypeIdentifier("irma-demo.RU.foo"): struct{}{},
irma.NewCredentialTypeIdentifier("irma-demo.baz.qux"): struct{}{}, irma.NewCredentialTypeIdentifier("irma-demo.baz.qux"): struct{}{},
}, },
AttributeTypes: map[irma.AttributeTypeIdentifier]struct{}{},
}, },
} }
require.True(t, reflect.DeepEqual(expectedErr, err), "Download() returned incorrect missing identifier set") require.True(t, reflect.DeepEqual(expectedErr, err), "Download() returned incorrect missing identifier set")
......
...@@ -87,6 +87,11 @@ type UnknownIdentifierError struct { ...@@ -87,6 +87,11 @@ type UnknownIdentifierError struct {
Missing *IrmaIdentifierSet Missing *IrmaIdentifierSet
} }
type RequiredAttributeMissingError struct {
ErrorType
Missing *IrmaIdentifierSet
}
const ( const (
SchemeManagerStatusValid = SchemeManagerStatus("Valid") SchemeManagerStatusValid = SchemeManagerStatus("Valid")
SchemeManagerStatusUnprocessed = SchemeManagerStatus("Unprocessed") SchemeManagerStatusUnprocessed = SchemeManagerStatus("Unprocessed")
...@@ -831,18 +836,22 @@ func (conf *Configuration) DownloadSchemeManagerSignature(manager *SchemeManager ...@@ -831,18 +836,22 @@ func (conf *Configuration) DownloadSchemeManagerSignature(manager *SchemeManager
} }
func (e *UnknownIdentifierError) Error() string { func (e *UnknownIdentifierError) Error() string {
return "unknown identifiers: " + e.Missing.String() return "Unknown identifiers: " + e.Missing.String()
}
func (e *RequiredAttributeMissingError) Error() string {
return "Required attributes are missing: " + e.Missing.String()
} }
// Download downloads the issuers, credential types and public keys specified in set // Download downloads the issuers, credential types and public keys specified in set
// if the current Configuration does not already have them, and checks their authenticity // if the current Configuration does not already have them, and checks their authenticity
// using the scheme manager index. // using the scheme manager index.
func (conf *Configuration) Download(session SessionRequest) (downloaded *IrmaIdentifierSet, err error) { func (conf *Configuration) Download(session SessionRequest) (downloaded *IrmaIdentifierSet, err error) {
if conf.readOnly { if conf.readOnly {
return nil, errors.New("cannot download into a read-only configuration") return nil, errors.New("Cannot download into a read-only configuration")
} }
missing, err := conf.checkIdentifiers(session) missing, requiredMissing, err := conf.checkIdentifiers(session)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -850,61 +859,76 @@ func (conf *Configuration) Download(session SessionRequest) (downloaded *IrmaIde ...@@ -850,61 +859,76 @@ func (conf *Configuration) Download(session SessionRequest) (downloaded *IrmaIde
return nil, &UnknownIdentifierError{ErrorUnknownSchemeManager, missing} return nil, &UnknownIdentifierError{ErrorUnknownSchemeManager, missing}
} }
// Update the scheme found above and parse them, if necessary // Update the scheme found above and parse, if necessary
downloaded = newIrmaIdentifierSet() downloaded = newIrmaIdentifierSet()
for id := range missing.allSchemes() {
// Combine to find all identifiers that possibly require updating, i.e.,
// ones that are not found in the configuration or,
// ones that were tagged non-optional, but were tagged optional in a more recent configuration
allMissing := newIrmaIdentifierSet()
allMissing.join(missing)
allMissing.join(requiredMissing)
// Try updating them
for id := range allMissing.allSchemes() {
if err = conf.UpdateSchemeManager(id, downloaded); err != nil { if err = conf.UpdateSchemeManager(id, downloaded); err != nil {
return return
} }
} }
if !downloaded.Empty() { if !downloaded.Empty() {
if err = conf.ParseFolder(); err != nil { if err = conf.ParseFolder(); err != nil {
return nil, err return nil, err
} }
} }
// Check again if all identifiers are known in the Configuration // Check again if all session identifiers are known now and required attributes are present
if missing, err = conf.checkIdentifiers(session); err != nil { missing, requiredMissing, err = conf.checkIdentifiers(session)
if err != nil {
return nil, err return nil, err
} }
// Required in the request, but not found in the configuration
if !missing.Empty() { if !missing.Empty() {
return nil, &UnknownIdentifierError{ErrorUnknownIdentifier, missing} return nil, &UnknownIdentifierError{ErrorUnknownIdentifier, missing}
} }
// (Still) required in the configuration, but not in the request
if !requiredMissing.Empty() {
return nil, &RequiredAttributeMissingError{ErrorRequiredAttributeMissing, requiredMissing}
}
return return
} }
func (conf *Configuration) checkCredentialTypes(session SessionRequest, missing *IrmaIdentifierSet) { func (conf *Configuration) checkCredentialTypes(session SessionRequest, missing *IrmaIdentifierSet, requiredMissing *IrmaIdentifierSet) {
var typ *CredentialType var typ *CredentialType
var contains bool var contains bool
switch s := session.(type) { switch s := session.(type) {
case *IssuanceRequest: case *IssuanceRequest:
for _, credreq := range s.Credentials { for _, credreq := range s.Credentials {
// First check if we have this credential type // First check if we have this credential type
typ, contains = conf.CredentialTypes[credreq.CredentialTypeID] typ, contains = conf.CredentialTypes[credreq.CredentialTypeID]
if !contains { if !contains {
missing.CredentialTypes[credreq.CredentialTypeID] = struct{}{} missing.CredentialTypes[credreq.CredentialTypeID] = struct{}{}
continue continue
} }
newAttrs := make(map[string]string)
for k, v := range credreq.Attributes { // Check for attributes in the request that are not in the credential configuration
newAttrs[k] = v for reqAttr, _ := range credreq.Attributes {
} attrID := NewAttributeTypeIdentifier(credreq.CredentialTypeID.String() + "." + reqAttr)
// For each of the attributes in the credentialtype, see if it is present; if so remove it from newAttrs if !typ.ContainsAttribute(attrID) {
// If not, check that it is optional; if not the credentialtype must be updated missing.AttributeTypes[attrID] = struct{}{}
for _, attrtyp := range typ.AttributeTypes {
_, contains = newAttrs[attrtyp.ID]
if !contains && !attrtyp.IsOptional() {
missing.CredentialTypes[credreq.CredentialTypeID] = struct{}{}
break
} }
delete(newAttrs, attrtyp.ID)
} }
// If there is anything left in newAttrs, then these are attributes that are not in the credentialtype
if len(newAttrs) > 0 { // Check if all attributes from the configuration are present, unless they are marked as optional
missing.CredentialTypes[credreq.CredentialTypeID] = struct{}{} for _, attrtype := range typ.AttributeTypes {
_, present := credreq.Attributes[attrtype.ID]
if !present && !attrtype.IsOptional() {
requiredMissing.AttributeTypes[attrtype.GetAttributeTypeIdentifier()] = struct{}{}
}
} }
} }
} }
...@@ -916,7 +940,7 @@ func (conf *Configuration) checkCredentialTypes(session SessionRequest, missing ...@@ -916,7 +940,7 @@ func (conf *Configuration) checkCredentialTypes(session SessionRequest, missing
return nil return nil
} }
if !attr.Type.IsCredential() && !typ.ContainsAttribute(attr.Type) { if !attr.Type.IsCredential() && !typ.ContainsAttribute(attr.Type) {
missing.CredentialTypes[credid] = struct{}{} missing.AttributeTypes[attr.Type] = struct{}{}
} }
return nil return nil
}) })
...@@ -924,14 +948,15 @@ func (conf *Configuration) checkCredentialTypes(session SessionRequest, missing ...@@ -924,14 +948,15 @@ func (conf *Configuration) checkCredentialTypes(session SessionRequest, missing
return return
} }
func (conf *Configuration) checkIdentifiers(session SessionRequest) (*IrmaIdentifierSet, error) { func (conf *Configuration) checkIdentifiers(session SessionRequest) (*IrmaIdentifierSet, *IrmaIdentifierSet, error) {
missing := newIrmaIdentifierSet() missing := newIrmaIdentifierSet()
requiredMissing := newIrmaIdentifierSet()
conf.checkSchemes(session, missing) conf.checkSchemes(session, missing)
if err := conf.checkIssuers(session.Identifiers(), missing); err != nil { if err := conf.checkIssuers(session.Identifiers(), missing); err != nil {
return nil, err return nil, nil, err
} }
conf.checkCredentialTypes(session, missing) conf.checkCredentialTypes(session, missing, requiredMissing)
return missing, nil return missing, requiredMissing, nil
} }
// CheckSchemes verifies that all schemes occuring in the specified session request occur in this // CheckSchemes verifies that all schemes occuring in the specified session request occur in this
...@@ -1290,6 +1315,7 @@ func (conf *Configuration) UpdateSchemes() error { ...@@ -1290,6 +1315,7 @@ func (conf *Configuration) UpdateSchemes() error {
SchemeManagers: map[SchemeManagerIdentifier]struct{}{}, SchemeManagers: map[SchemeManagerIdentifier]struct{}{},
Issuers: map[IssuerIdentifier]struct{}{}, Issuers: map[IssuerIdentifier]struct{}{},
CredentialTypes: map[CredentialTypeIdentifier]struct{}{}, CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
AttributeTypes: map[AttributeTypeIdentifier]struct{}{},
} }
for id := range conf.SchemeManagers { for id := range conf.SchemeManagers {
Logger.WithField("scheme", id).Info("Auto-updating scheme") Logger.WithField("scheme", id).Info("Auto-updating scheme")
......
...@@ -193,6 +193,8 @@ const ( ...@@ -193,6 +193,8 @@ const (
ErrorServerResponse = ErrorType("serverResponse") ErrorServerResponse = ErrorType("serverResponse")
// Credential type not present in our Configuration // Credential type not present in our Configuration
ErrorUnknownIdentifier = ErrorType("unknownIdentifier") ErrorUnknownIdentifier = ErrorType("unknownIdentifier")
// Non-optional attribute not present in credential
ErrorRequiredAttributeMissing = ErrorType("requiredAttributeMissing")
// Error during downloading of credential type, issuer, or public keys // Error during downloading of credential type, issuer, or public keys
ErrorConfigurationDownload = ErrorType("configurationDownload") ErrorConfigurationDownload = ErrorType("configurationDownload")
// IRMA requests refers to unknown scheme manager // IRMA requests refers to unknown scheme manager
......
...@@ -417,6 +417,7 @@ func (dr *DisclosureRequest) identifiers() *IrmaIdentifierSet { ...@@ -417,6 +417,7 @@ func (dr *DisclosureRequest) identifiers() *IrmaIdentifierSet {
Issuers: map[IssuerIdentifier]struct{}{}, Issuers: map[IssuerIdentifier]struct{}{},
CredentialTypes: map[CredentialTypeIdentifier]struct{}{}, CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
PublicKeys: map[IssuerIdentifier][]int{}, PublicKeys: map[IssuerIdentifier][]int{},
AttributeTypes: map[AttributeTypeIdentifier]struct{}{},
} }
_ = dr.Disclose.Iterate(func(a *AttributeRequest) error { _ = dr.Disclose.Iterate(func(a *AttributeRequest) error {
...@@ -424,6 +425,7 @@ func (dr *DisclosureRequest) identifiers() *IrmaIdentifierSet { ...@@ -424,6 +425,7 @@ func (dr *DisclosureRequest) identifiers() *IrmaIdentifierSet {
ids.SchemeManagers[attr.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{} ids.SchemeManagers[attr.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
ids.Issuers[attr.CredentialTypeIdentifier().IssuerIdentifier()] = struct{}{} ids.Issuers[attr.CredentialTypeIdentifier().IssuerIdentifier()] = struct{}{}
ids.CredentialTypes[attr.CredentialTypeIdentifier()] = struct{}{} ids.CredentialTypes[attr.CredentialTypeIdentifier()] = struct{}{}
ids.AttributeTypes[attr] = struct{}{}
return nil return nil
}) })
...@@ -540,6 +542,7 @@ func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet { ...@@ -540,6 +542,7 @@ func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet {
SchemeManagers: map[SchemeManagerIdentifier]struct{}{}, SchemeManagers: map[SchemeManagerIdentifier]struct{}{},
Issuers: map[IssuerIdentifier]struct{}{}, Issuers: map[IssuerIdentifier]struct{}{},
CredentialTypes: map[CredentialTypeIdentifier]struct{}{}, CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
AttributeTypes: map[AttributeTypeIdentifier]struct{}{},
PublicKeys: map[IssuerIdentifier][]int{}, PublicKeys: map[IssuerIdentifier][]int{},
} }
...@@ -547,7 +550,11 @@ func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet { ...@@ -547,7 +550,11 @@ func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet {
issuer := credreq.CredentialTypeID.IssuerIdentifier() issuer := credreq.CredentialTypeID.IssuerIdentifier()
ir.ids.SchemeManagers[issuer.SchemeManagerIdentifier()] = struct{}{} ir.ids.SchemeManagers[issuer.SchemeManagerIdentifier()] = struct{}{}
ir.ids.Issuers[issuer] = struct{}{} ir.ids.Issuers[issuer] = struct{}{}
ir.ids.CredentialTypes[credreq.CredentialTypeID] = struct{}{} credID := credreq.CredentialTypeID
ir.ids.CredentialTypes[credID] = struct{}{}
for attr, _ := range credreq.Attributes { // this is kind of ugly
ir.ids.AttributeTypes[NewAttributeTypeIdentifier(credID.String()+"."+attr)] = struct{}{}
}
if ir.ids.PublicKeys[issuer] == nil { if ir.ids.PublicKeys[issuer] == nil {
ir.ids.PublicKeys[issuer] = []int{} ir.ids.PublicKeys[issuer] = []int{}
} }
......
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