EdgexAgent/device-ble-go/vendor/github.com/openziti/sdk-golang/edge-apis/credentials.go
2025-07-10 20:40:32 +08:00

357 lines
11 KiB
Go

package edge_apis
import (
"crypto"
"crypto/tls"
"crypto/x509"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/openziti/edge-api/rest_model"
"github.com/openziti/identity"
"github.com/openziti/sdk-golang/ziti/edge/network"
"github.com/openziti/sdk-golang/ziti/sdkinfo"
"net/http"
)
// Credentials represents the minimal information needed across all authentication mechanisms to authenticate an identity
// to an OpenZiti network.
type Credentials interface {
// Payload constructs the objects that represent the JSON authentication payload for this set of credentials.
Payload() *rest_model.Authenticate
// TlsCerts returns zero or more tls.Certificates used for client authentication.
TlsCerts() []tls.Certificate
// GetCaPool returns the CA pool that this credential was configured to trust.
GetCaPool() *x509.CertPool
// Method returns the authentication necessary to complete an authentication request.
Method() string
// AddAuthHeader adds a header for all authentication requests.
AddAuthHeader(key, value string)
// AddRequestHeader adds a header for all requests after authentication
AddRequestHeader(key, value string)
// AddJWT adds additional JWTs to the credentials. Used to satisfy secondary authentication/MFA requirements. The
// provided token should be the base64 encoded version of the token.
AddJWT(string)
// ClientAuthInfoWriter is used to pass a Credentials instance to the openapi runtime to authenticate outgoing
//requests.
runtime.ClientAuthInfoWriter
// GetRequestHeaders returns a set of headers to use after authentication during normal HTTP operations
GetRequestHeaders() http.Header
}
// IdentityProvider is a sentinel interface used to determine whether the backing Credentials instance can provide
// an Identity that can provide a certificate and private key used to initiate mTLS connections.
type IdentityProvider interface {
GetIdentity() identity.Identity
}
// toTlsCerts converts an array of certificates into a single tls.Certificate. Index zero is assumed to be the leaf
// certificate and all subsequent certificates to be the support certificate chain that should be sent to servers.
// At least one certificate must be provided.
func toTlsCerts(certs []*x509.Certificate, key crypto.PrivateKey) tls.Certificate {
tlsCert := tls.Certificate{
PrivateKey: key,
Leaf: certs[0],
}
for _, cert := range certs {
tlsCert.Certificate = append(tlsCert.Certificate, cert.Raw)
}
return tlsCert
}
// getClientAuthInfoOp returns a one-off runtime.ClientOperation used to authenticate single requests without altering
// the authentication operation of the entire client runtime.
func getClientAuthInfoOp(credentials Credentials, client *http.Client) func(*runtime.ClientOperation) {
return func(operation *runtime.ClientOperation) {
operation.AuthInfo = credentials
certs := credentials.TlsCerts()
if len(certs) != 0 {
operation.Client = client
if transport, ok := operation.Client.Transport.(*http.Transport); ok {
transport.TLSClientConfig.Certificates = certs
}
}
}
}
// BaseCredentials is a shared struct of information all Credentials implementations require.
type BaseCredentials struct {
// ConfigTypes is used to set the configuration types for services during authentication
ConfigTypes []string
// AuthHeaders is a map of strings to string arrays of headers to send with auth requests.
AuthHeaders http.Header
// RequestHeaders is a map of string to string arrays of headers to send on non-authentication requests.
RequestHeaders http.Header
// EnvInfo is provided during authentication to set environmental information about the client.
EnvInfo *rest_model.EnvInfo
// SdkInfo is provided during authentication to set SDK information about the client.
SdkInfo *rest_model.SdkInfo
// CaPool will override the client's default certificate pool if set to a non-nil value.
CaPool *x509.CertPool
}
// Payload will produce the object used to construct the body of an authentication requests. The base version
// sets shared information available in BaseCredentials.
func (c *BaseCredentials) Payload() *rest_model.Authenticate {
envInfo, sdkInfo := sdkinfo.GetSdkInfo()
if c.EnvInfo != nil {
envInfo = c.EnvInfo
}
if c.SdkInfo != nil {
sdkInfo = c.SdkInfo
}
return &rest_model.Authenticate{
ConfigTypes: c.ConfigTypes,
EnvInfo: envInfo,
SdkInfo: sdkInfo,
}
}
// GetCaPool provides a base implementation to return the certificate pool of a Credentials instance.
func (c *BaseCredentials) GetCaPool() *x509.CertPool {
return c.CaPool
}
// AddAuthHeader provides a base implementation to add a header to authentication requests.
func (c *BaseCredentials) AddAuthHeader(key, value string) {
if c.AuthHeaders == nil {
c.AuthHeaders = http.Header{}
}
c.AuthHeaders.Add(key, value)
}
// AddRequestHeader provides a base implementation to add a header to all requests after authentication.
func (c *BaseCredentials) AddRequestHeader(key, value string) {
if c.RequestHeaders == nil {
c.RequestHeaders = http.Header{}
}
c.RequestHeaders.Add(key, value)
}
// AddJWT adds additional JWTs to the credentials. Used to satisfy secondary authentication/MFA requirements. The
// provided token should be the base64 encoded version of the token. Convenience function for AddHeader.
func (c *BaseCredentials) AddJWT(token string) {
c.AddAuthHeader("Authorization", "Bearer "+token)
c.AddRequestHeader("Authorization", "Bearer "+token)
}
// AuthenticateRequest provides a base implementation to authenticate an outgoing request. This is provided here
// for authentication methods such as `cert` which do not have to provide any more request level information.
func (c *BaseCredentials) AuthenticateRequest(request runtime.ClientRequest, _ strfmt.Registry) error {
var errors []error
for hName, hVals := range c.AuthHeaders {
for _, hVal := range hVals {
err := request.SetHeaderParam(hName, hVal)
if err != nil {
errors = append(errors, err)
}
}
}
if len(errors) > 0 {
return network.MultipleErrors(errors)
}
return nil
}
// ProcessRequest proves a base implemmentation mutate runtime.ClientRequests as they are sent out after
// authentication. Useful for adding headers.
func (c *BaseCredentials) ProcessRequest(request runtime.ClientRequest, _ strfmt.Registry) error {
var errors []error
for hName, hVals := range c.RequestHeaders {
for _, hVal := range hVals {
err := request.SetHeaderParam(hName, hVal)
if err != nil {
errors = append(errors, err)
}
}
}
if len(errors) > 0 {
return network.MultipleErrors(errors)
}
return nil
}
// GetRequestHeaders returns headers that should be sent on requests post authentication.
func (c *BaseCredentials) GetRequestHeaders() http.Header {
return c.RequestHeaders
}
// TlsCerts provides a base implementation of returning the tls.Certificate array that will be used to setup
// mTLS connections. This is provided here for authentication methods that do not initially require mTLS (e.g. JWTs).
func (c *BaseCredentials) TlsCerts() []tls.Certificate {
return nil
}
var _ Credentials = &CertCredentials{}
// CertCredentials represents authentication using certificates that are not from an Identity configuration file.
type CertCredentials struct {
BaseCredentials
Certs []*x509.Certificate
Key crypto.PrivateKey
}
// NewCertCredentials creates Credentials instance based upon an array of certificates. At least one certificate must
// be provided and the certificate at index zero is assumed to be the leaf client certificate that pairs with the
// provided private key. All other certificates are assumed to support the leaf client certificate as a chain.
func NewCertCredentials(certs []*x509.Certificate, key crypto.PrivateKey) *CertCredentials {
return &CertCredentials{
BaseCredentials: BaseCredentials{},
Certs: certs,
Key: key,
}
}
func (c *CertCredentials) Method() string {
return "cert"
}
func (c *CertCredentials) TlsCerts() []tls.Certificate {
return []tls.Certificate{toTlsCerts(c.Certs, c.Key)}
}
func (c *CertCredentials) GetIdentity() identity.Identity {
return identity.NewClientTokenIdentityWithPool(c.Certs, c.Key, c.GetCaPool())
}
var _ Credentials = &IdentityCredentials{}
type IdentityCredentials struct {
BaseCredentials
Identity identity.Identity
}
// NewIdentityCredentials creates a Credentials instance based upon and Identity.
func NewIdentityCredentials(identity identity.Identity) *IdentityCredentials {
return &IdentityCredentials{
BaseCredentials: BaseCredentials{},
Identity: identity,
}
}
// NewIdentityCredentialsFromConfig creates a Credentials instance based upon and Identity configuration.
func NewIdentityCredentialsFromConfig(config identity.Config) *IdentityCredentials {
return &IdentityCredentials{
BaseCredentials: BaseCredentials{},
Identity: &identity.LazyIdentity{Config: &config},
}
}
func (c *IdentityCredentials) GetIdentity() identity.Identity {
return c.Identity
}
func (c *IdentityCredentials) Method() string {
return "cert"
}
func (c *IdentityCredentials) GetCaPool() *x509.CertPool {
return c.Identity.CA()
}
func (c *IdentityCredentials) TlsCerts() []tls.Certificate {
tlsCert := c.Identity.Cert()
if tlsCert != nil {
return []tls.Certificate{*tlsCert}
}
return nil
}
func (c *IdentityCredentials) AuthenticateRequest(request runtime.ClientRequest, reg strfmt.Registry) error {
return c.BaseCredentials.AuthenticateRequest(request, reg)
}
var _ Credentials = &JwtCredentials{}
type JwtCredentials struct {
BaseCredentials
JWT string
SendOnEveryRequest bool
}
// NewJwtCredentials creates a Credentials instance based on a JWT obtained from an outside system.
func NewJwtCredentials(jwt string) *JwtCredentials {
return &JwtCredentials{
BaseCredentials: BaseCredentials{},
JWT: jwt,
}
}
func (c *JwtCredentials) Method() string {
return "ext-jwt"
}
func (c *JwtCredentials) AuthenticateRequest(request runtime.ClientRequest, reg strfmt.Registry) error {
var errors []error
err := c.BaseCredentials.AuthenticateRequest(request, reg)
if err != nil {
errors = append(errors, err)
}
err = request.SetHeaderParam("Authorization", "Bearer "+c.JWT)
if err != nil {
errors = append(errors, err)
}
if len(errors) > 0 {
return network.MultipleErrors(errors)
}
return nil
}
var _ Credentials = &UpdbCredentials{}
type UpdbCredentials struct {
BaseCredentials
Username string
Password string
}
func (c *UpdbCredentials) Method() string {
return "password"
}
// NewUpdbCredentials creates a Credentials instance based on a username/passwords combination.
func NewUpdbCredentials(username string, password string) *UpdbCredentials {
return &UpdbCredentials{
BaseCredentials: BaseCredentials{},
Username: username,
Password: password,
}
}
func (c *UpdbCredentials) Payload() *rest_model.Authenticate {
payload := c.BaseCredentials.Payload()
payload.Username = rest_model.Username(c.Username)
payload.Password = rest_model.Password(c.Password)
return payload
}
func (c *UpdbCredentials) AuthenticateRequest(request runtime.ClientRequest, reg strfmt.Registry) error {
return c.BaseCredentials.AuthenticateRequest(request, reg)
}