lib/ga4gh/visa_linked_identities.go (84 lines of code) (raw):
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ga4gh
import (
"fmt"
"net/url"
"strings"
glog "github.com/golang/glog" /* copybara-comment */
)
// ID identifies a subject at an issuer.
type ID struct {
Issuer string
Subject string
}
// CheckLinkedIDs checks if there are sufficient LinkedIdentities Assertions to
// show that all IDs of the given list of Visas are the same.
func CheckLinkedIDs(vs []*Visa) error {
var ids []ID
for _, v := range vs {
ids = append(ids, ID{v.Data().Issuer, v.Data().Subject})
}
// links contains which ids are linked together.
links := make(map[ID][]ID)
for _, v := range vs {
x := ID{v.Data().Issuer, v.Data().Subject}
for _, y := range ExtractLinkedIDs(v.Data().Assertion) {
links[x] = append(links[x], y)
links[y] = append(links[y], x)
}
}
return connectedIDs(ids, links)
}
func connectedIDs(ids []ID, links map[ID][]ID) error {
glog.V(1).Infof("connectedIDs(%+v,%+v)", ids, links)
if len(ids) < 2 {
return nil
}
// BFS
mark := make(map[ID]bool)
queue := make(chan ID, len(ids))
defer close(queue)
queue <- ids[0]
for len(queue) > 0 {
x := <-queue
mark[x] = true
for _, y := range links[x] {
if !mark[y] {
queue <- y
}
}
}
if len(mark) != len(ids) {
return fmt.Errorf("identities on the visas are not connected")
}
return nil
}
// ExtractLinkedIDs extracts ids from a LinkedIdentities Assertion. Format is a
// semicolon separated list, each item in the list is of the form "subject,issuer"
// where subject and issuer are URI-encoded.
// http://bit.ly/ga4gh-passport-v1#linkedidentities
func ExtractLinkedIDs(a Assertion) []ID {
glog.V(1).Info("ExtractLinkedIDs")
if a.Type != LinkedIdentities {
return nil
}
items := strings.Split(string(a.Value), ";")
var res []ID
for _, item := range items {
parts := strings.Split(item, ",")
if len(parts) != 2 {
glog.Warningf("invalid value for LinkedIdentities assertion: %+v", a.Value)
return nil
}
issuer, err := url.PathUnescape(parts[1])
if err != nil {
glog.Warningf("invalid value for LinkedIdentities assertion: %+v", a.Value)
return nil
}
subject, err := url.PathUnescape(parts[0])
if err != nil {
glog.Warningf("invalid value for LinkedIdentities assertion: %+v", a.Value)
return nil
}
res = append(res, ID{Issuer: issuer, Subject: subject})
}
return res
}
// LinkedIDValue creates a LinkedIdentities Assertion Value from a given list of
// IDs.
func LinkedIDValue(neighbors []ID) Value {
glog.V(1).Info("LinkedIDValue")
var ids []string
for _, n := range neighbors {
ids = append(ids, url.PathEscape(n.Subject)+","+url.PathEscape(n.Issuer))
}
return Value(strings.Join(ids, ";"))
}