lib/ga4gh/type.go (100 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"
"regexp"
"strings"
)
// Timestamp is the number of seconds since epoch.
type Timestamp = int64
// Type is known GA4GH Assertion types.
// http://bit.ly/ga4gh-passport-v1#type
type Type string
const (
// AffiliationAndRole Assertion type.
// http://bit.ly/ga4gh-passport-v1#affiliationandrole
AffiliationAndRole Type = "AffiliationAndRole"
// AcceptedTermsAndPolicies Assertion type.
// http://bit.ly/ga4gh-passport-v1#acceptedtermsandpolicies
AcceptedTermsAndPolicies Type = "AcceptedTermsAndPolicies"
// ResearcherStatus Assertion type.
// http://bit.ly/ga4gh-passport-v1#researcherstatus
ResearcherStatus Type = "ResearcherStatus"
// ControlledAccessGrants Assertion type.
// http://bit.ly/ga4gh-passport-v1#controlledaccessgrants
ControlledAccessGrants Type = "ControlledAccessGrants"
// LinkedIdentities Assertion type.
// http://bit.ly/ga4gh-passport-v1#linkedidentities
LinkedIdentities Type = "LinkedIdentities"
)
// ValidType checks the Type of an Assertion is valid.
func ValidType(t Type) bool {
return StandardType(t) || CustomType(t)
}
// StandardType checks if the Type of an Assertion is one of the standard ones.
// http://bit.ly/ga4gh-passport-v1#ga4gh-standard-passport-visa-type-definitions
func StandardType(t Type) bool {
switch t {
case AffiliationAndRole, AcceptedTermsAndPolicies, ResearcherStatus, ControlledAccessGrants, LinkedIdentities:
return true
default:
return false
}
}
// CustomType checks if the Type of an Assertion is a custom.
// http://bit.ly/ga4gh-passport-v1#custom-passport-visa-types
// http://bit.ly/ga4gh-passport-v1#url-fields
func CustomType(t Type) bool {
if _, err := url.ParseRequestURI(string(t)); err != nil {
return false
}
return true
}
// Value is the value of an Assertion.
// http://bit.ly/ga4gh-passport-v1#value
type Value string
// By is the By of an Assertion.
// http://bit.ly/ga4gh-passport-v1#by
type By string
const (
// Self is the Pasport Visa Identity for which the assertion is being made and the person who made the assertion is the same person.
Self By = "self"
// Peer is a person at the source organization has made this assertion on behalf of the Passport Visa Identity's person, and the person who is making the assertion has the same Passport Visa Type and value in that source organization. The source field represents the peer’s organization that is making the assertion, which is not necessarily the same organization as the Passport Visa Identity's organization.
Peer By = "peer"
// System is the source organization’s information system has made the assertion based on system data or metadata that it stores.
System By = "system"
// SO is a person (also known as "signing official") making the assertion within the source organization possesses direct authority (as part of their formal duties) to bind the organization to their assertion that the Passport Visa Identity, did possess such authority at the time the assertion was made.
SO By = "so"
// DAC is a Data Access Committee or other authority that is responsible as a grantee decision-maker for the given value and source field pair.)
DAC By = "dac"
)
// Source is the Source of an Assertion.
// http://bit.ly/ga4gh-passport-v1#source
type Source string
// Pattern for a string from Pattern Matching section of GA4GH Passport sepcification.
// Pattern should be of one of the following forms:
// prefix = "const:": the field should be equal to the suffix
// prefix = "pattern:": the field should match the suffix
// prefix = "split_pattern": the field should match one of the parts of suffix after splitting by ;
// The only wildchars for matching are ? and *.
// ? is interpreted as any single character, * is interpretted as any string.
// http://bit.ly/ga4gh-passport-v1#pattern-matching
type Pattern string
// MatchPatterns checks if a given string matches a Pattern.
func MatchPatterns(p Pattern, v string) error {
switch {
case p == "": // No pattern is specified.
return nil
case strings.HasPrefix(string(p), "const:"):
w := string(p[len("const:"):])
if w != v {
return fmt.Errorf("const not matched: %q %q", p, v)
}
return nil
case strings.HasPrefix(string(p), "pattern:"):
w := string(p[len("pattern:"):])
if matchSuffix(w, v) != nil {
return fmt.Errorf("pattern not matched: %q %q", p, v)
}
return nil
case strings.HasPrefix(string(p), "split_pattern:"):
ws := strings.Split(string(p[len("split_pattern:"):]), ";")
for _, w := range ws {
if matchSuffix(w, v) == nil {
return nil
}
}
return fmt.Errorf("split_pattern not matched: %q %q", p, v)
default:
return fmt.Errorf("unkown pattern")
}
}
// matchSuffix gets a suffix for a pattern and checks if v matches it.
func matchSuffix(w string, v string) error {
all := regexp.QuoteMeta("*")
any := regexp.QuoteMeta("?")
q := regexp.QuoteMeta(w)
// Replace the quoted all ("*") with the regex equivalent (".*")
q = strings.ReplaceAll(q, all, ".*")
// Replace the quoted any ("?") with the regex value (".")
q = strings.ReplaceAll(q, any, ".")
q = "^" + q + "$"
// TODO: use global regexp cache.
r, err := regexp.Compile(q)
if err != nil {
return fmt.Errorf("invalid pattern matching strings: %q", w)
}
if !r.MatchString(v) {
return fmt.Errorf("pattern not matched:%q %q", w, v)
}
return nil
}
// RegExp is a RE2 string.
// https://golang.org/s/re2syntax
type RegExp string
// MatchRegExp checks if a value matches one of the given list of RE2s.
func MatchRegExp(e RegExp, x Value) bool {
// TODO: add a (global) cache for r.
r, err := regexp.Compile(string(e))
if err != nil {
return false
}
if r.MatchString(string(x)) {
return true
}
return false
}
// Scope is the AAI Scope claim
// http://bit.ly/ga4gh-aai-profile#ga4gh-jwt-format
type Scope string