EdgexAgent/device-gps-go/vendor/github.com/spiffe/go-spiffe/v2/spiffeid/id.go
2025-07-10 20:30:06 +08:00

259 lines
7.4 KiB
Go

package spiffeid
import (
"errors"
"fmt"
"net/url"
"strings"
)
const (
schemePrefix = "spiffe://"
schemePrefixLen = len(schemePrefix)
)
// FromPath returns a new SPIFFE ID in the given trust domain and with the
// given path. The supplied path must be a valid absolute path according to the
// SPIFFE specification.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func FromPath(td TrustDomain, path string) (ID, error) {
if err := ValidatePath(path); err != nil {
return ID{}, err
}
return makeID(td, path)
}
// FromPathf returns a new SPIFFE ID from the formatted path in the given trust
// domain. The formatted path must be a valid absolute path according to the
// SPIFFE specification.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func FromPathf(td TrustDomain, format string, args ...interface{}) (ID, error) {
path, err := FormatPath(format, args...)
if err != nil {
return ID{}, err
}
return makeID(td, path)
}
// FromSegments returns a new SPIFFE ID in the given trust domain with joined
// path segments. The path segments must be valid according to the SPIFFE
// specification and must not contain path separators.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func FromSegments(td TrustDomain, segments ...string) (ID, error) {
path, err := JoinPathSegments(segments...)
if err != nil {
return ID{}, err
}
return makeID(td, path)
}
// FromString parses a SPIFFE ID from a string.
func FromString(id string) (ID, error) {
switch {
case id == "":
return ID{}, errEmpty
case !strings.HasPrefix(id, schemePrefix):
return ID{}, errWrongScheme
}
pathidx := schemePrefixLen
for ; pathidx < len(id); pathidx++ {
c := id[pathidx]
if c == '/' {
break
}
if !isValidTrustDomainChar(c) {
return ID{}, errBadTrustDomainChar
}
}
if pathidx == schemePrefixLen {
return ID{}, errMissingTrustDomain
}
if err := ValidatePath(id[pathidx:]); err != nil {
return ID{}, err
}
return ID{
id: id,
pathidx: pathidx,
}, nil
}
// FromStringf parses a SPIFFE ID from a formatted string.
func FromStringf(format string, args ...interface{}) (ID, error) {
return FromString(fmt.Sprintf(format, args...))
}
// FromURI parses a SPIFFE ID from a URI.
func FromURI(uri *url.URL) (ID, error) {
return FromString(uri.String())
}
// ID is a SPIFFE ID
type ID struct {
id string
// pathidx tracks the index to the beginning of the path inside of id. This
// is used when extracting the trust domain or path portions of the id.
pathidx int
}
// TrustDomain returns the trust domain of the SPIFFE ID.
func (id ID) TrustDomain() TrustDomain {
if id.IsZero() {
return TrustDomain{}
}
return TrustDomain{name: id.id[schemePrefixLen:id.pathidx]}
}
// MemberOf returns true if the SPIFFE ID is a member of the given trust domain.
func (id ID) MemberOf(td TrustDomain) bool {
return id.TrustDomain() == td
}
// Path returns the path of the SPIFFE ID inside the trust domain.
func (id ID) Path() string {
return id.id[id.pathidx:]
}
// String returns the string representation of the SPIFFE ID, e.g.,
// "spiffe://example.org/foo/bar".
func (id ID) String() string {
return id.id
}
// URL returns a URL for SPIFFE ID.
func (id ID) URL() *url.URL {
if id.IsZero() {
return &url.URL{}
}
return &url.URL{
Scheme: "spiffe",
Host: id.TrustDomain().String(),
Path: id.Path(),
}
}
// IsZero returns true if the SPIFFE ID is the zero value.
func (id ID) IsZero() bool {
return id.id == ""
}
// AppendPath returns an ID with the appended path. It will fail if called on a
// zero value. The path to append must be a valid absolute path according to
// the SPIFFE specification.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func (id ID) AppendPath(path string) (ID, error) {
if id.IsZero() {
return ID{}, errors.New("cannot append path on a zero ID value")
}
if err := ValidatePath(path); err != nil {
return ID{}, err
}
id.id += path
return id, nil
}
// AppendPathf returns an ID with the appended formatted path. It will fail if
// called on a zero value. The formatted path must be a valid absolute path
// according to the SPIFFE specification.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func (id ID) AppendPathf(format string, args ...interface{}) (ID, error) {
if id.IsZero() {
return ID{}, errors.New("cannot append path on a zero ID value")
}
path, err := FormatPath(format, args...)
if err != nil {
return ID{}, err
}
id.id += path
return id, nil
}
// AppendSegments returns an ID with the appended joined path segments. It
// will fail if called on a zero value. The path segments must be valid
// according to the SPIFFE specification and must not contain path separators.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func (id ID) AppendSegments(segments ...string) (ID, error) {
if id.IsZero() {
return ID{}, errors.New("cannot append path segments on a zero ID value")
}
path, err := JoinPathSegments(segments...)
if err != nil {
return ID{}, err
}
id.id += path
return id, nil
}
// Replace path returns an ID with the given path in the same trust domain. It
// will fail if called on a zero value. The given path must be a valid absolute
// path according to the SPIFFE specification.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func (id ID) ReplacePath(path string) (ID, error) {
if id.IsZero() {
return ID{}, errors.New("cannot replace path on a zero ID value")
}
return FromPath(id.TrustDomain(), path)
}
// ReplacePathf returns an ID with the formatted path in the same trust domain.
// It will fail if called on a zero value. The formatted path must be a valid
// absolute path according to the SPIFFE specification.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func (id ID) ReplacePathf(format string, args ...interface{}) (ID, error) {
if id.IsZero() {
return ID{}, errors.New("cannot replace path on a zero ID value")
}
return FromPathf(id.TrustDomain(), format, args...)
}
// ReplaceSegments returns an ID with the joined path segments in the same
// trust domain. It will fail if called on a zero value. The path segments must
// be valid according to the SPIFFE specification and must not contain path
// separators.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func (id ID) ReplaceSegments(segments ...string) (ID, error) {
if id.IsZero() {
return ID{}, errors.New("cannot replace path segments on a zero ID value")
}
return FromSegments(id.TrustDomain(), segments...)
}
// MarshalText returns a text representation of the ID. If the ID is the zero
// value, nil is returned.
func (id ID) MarshalText() ([]byte, error) {
if id.IsZero() {
return nil, nil
}
return []byte(id.String()), nil
}
// UnmarshalText decodes a text representation of the ID. If the text is empty,
// the ID is set to the zero value.
func (id *ID) UnmarshalText(text []byte) error {
if len(text) == 0 {
*id = ID{}
return nil
}
unmarshaled, err := FromString(string(text))
if err != nil {
return err
}
*id = unmarshaled
return nil
}
func makeID(td TrustDomain, path string) (ID, error) {
if td.IsZero() {
return ID{}, errors.New("trust domain is empty")
}
return ID{
id: schemePrefix + td.name + path,
pathidx: schemePrefixLen + len(td.name),
}, nil
}