EdgexAgent/device-ble-go/vendor/github.com/openziti/edge-api/rest_util/authenticate.go
2025-07-10 20:40:32 +08:00

329 lines
9.0 KiB
Go

/*
Copyright NetFoundry Inc.
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
https://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 rest_util
import (
"context"
"crypto"
"crypto/tls"
"crypto/x509"
"fmt"
"github.com/go-openapi/runtime"
openapiclient "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
"github.com/openziti/edge-api/rest_management_api_client"
"github.com/openziti/edge-api/rest_management_api_client/authentication"
"github.com/openziti/edge-api/rest_model"
"net/http"
"net/url"
)
// Authenticator is an interface that facilitates obtaining an API Session.
type Authenticator interface {
//Authenticate issues an authentication HTTP requests to the designated controller. The method and operation
// of this authentication request is determined by the implementor.
Authenticate(controllerAddress *url.URL) (*rest_model.CurrentAPISessionDetail, error)
//BuildHttpClient returns a http.Client to use for an API client. This specifically allows
//client certificate authentication to be configured in the http.Client's transport/tls.Config
BuildHttpClient() (*http.Client, error)
//SetInfo sets the env and sdk info submitted on Authenticate
SetInfo(*rest_model.EnvInfo, *rest_model.SdkInfo)
}
// HttpClientFunc allows an external HttpClient to be created and used.
type HttpClientFunc func(tlsClientConfig *tls.Config) (*http.Client, error)
// TlsConfigFunc allows the tls.Config to be modified before use.
type TlsConfigFunc func() (*tls.Config, error)
// AuthenticatorBase provides embeddable shared capabilities for all
// authenticators.
type AuthenticatorBase struct {
ConfigTypes rest_model.ConfigTypes
EnvInfo *rest_model.EnvInfo
SdkInfo *rest_model.SdkInfo
HttpClientFunc HttpClientFunc
TlsConfigFunc TlsConfigFunc
RootCas *x509.CertPool
}
// BuildHttpClientWithModifyTls builds a new http.Client with the provided HttpClientFunc and TlsConfigFunc.
// If not set, default NewHttpClientWithTlsConfig and NewTlsConfig will be used.
func (a *AuthenticatorBase) BuildHttpClientWithModifyTls(modifyTls func(*tls.Config)) (*http.Client, error) {
if a.HttpClientFunc == nil {
a.HttpClientFunc = NewHttpClientWithTlsConfig
}
if a.TlsConfigFunc == nil {
a.TlsConfigFunc = NewTlsConfig
}
tlsConfig, err := a.TlsConfigFunc()
tlsConfig.RootCAs = a.RootCas
if modifyTls != nil {
modifyTls(tlsConfig)
}
if err != nil {
return nil, err
}
httpClient, err := a.HttpClientFunc(tlsConfig)
if err != nil {
return nil, err
}
return httpClient, err
}
func (a *AuthenticatorBase) SetInfo(env *rest_model.EnvInfo, sdk *rest_model.SdkInfo) {
a.EnvInfo = env
a.SdkInfo = sdk
}
var _ Authenticator = &AuthenticatorUpdb{}
// AuthenticatorUpdb is an implementation of Authenticator that can fulfill username/password authentication
// requests.
type AuthenticatorUpdb struct {
AuthenticatorBase
Username string
Password string
}
func NewAuthenticatorUpdb(username, password string) *AuthenticatorUpdb {
return &AuthenticatorUpdb{
Username: username,
Password: password,
}
}
func (a *AuthenticatorUpdb) BuildHttpClient() (*http.Client, error) {
return a.BuildHttpClientWithModifyTls(nil)
}
func (a *AuthenticatorUpdb) Params() *authentication.AuthenticateParams {
return &authentication.AuthenticateParams{
Auth: &rest_model.Authenticate{
ConfigTypes: a.ConfigTypes,
EnvInfo: a.EnvInfo,
SdkInfo: a.SdkInfo,
Username: rest_model.Username(a.Username),
Password: rest_model.Password(a.Password),
},
Method: "password",
Context: context.Background(),
}
}
func (a *AuthenticatorUpdb) Authenticate(controllerAddress *url.URL) (*rest_model.CurrentAPISessionDetail, error) {
httpClient, err := a.BuildHttpClientWithModifyTls(nil)
if err != nil {
return nil, err
}
path := rest_management_api_client.DefaultBasePath
if controllerAddress.Path != "" && controllerAddress.Path != "/" {
path = controllerAddress.Path
}
clientRuntime := openapiclient.NewWithClient(controllerAddress.Host, path, rest_management_api_client.DefaultSchemes, httpClient)
client := rest_management_api_client.New(clientRuntime, nil)
params := a.Params()
resp, err := client.Authentication.Authenticate(params)
if err != nil {
return nil, err
}
if resp.GetPayload() == nil {
return nil, fmt.Errorf("error, nil payload: %v", resp.Error())
}
return resp.GetPayload().Data, nil
}
var _ Authenticator = &AuthenticatorCert{}
// CertProvider scopes a subset of the identity.Identity interface
type CertProvider interface {
Cert() *tls.Certificate
CA() *x509.CertPool
ClientTLSConfig() *tls.Config
}
// AuthenticatorIdentity is meant to deal with OpenZiti identity files and interfaces defined in the `identity`
// repository
type AuthenticatorIdentity struct {
CertProvider
AuthenticatorBase
}
func (a *AuthenticatorIdentity) BuildHttpClient() (*http.Client, error) {
return a.BuildHttpClientWithModifyTls(func(config *tls.Config) {
src := a.CertProvider.ClientTLSConfig()
config.Certificates = src.Certificates
config.RootCAs = src.RootCAs
})
}
// AuthenticatorCert is an implementation of Authenticator that can fulfill client certificate authentication
// requests.
type AuthenticatorCert struct {
AuthenticatorBase
Certificate *x509.Certificate
PrivateKey crypto.PrivateKey
}
func NewAuthenticatorCert(cert *x509.Certificate, privateKey crypto.PrivateKey) *AuthenticatorCert {
return &AuthenticatorCert{
Certificate: cert,
PrivateKey: privateKey,
}
}
func (a *AuthenticatorCert) BuildHttpClient() (*http.Client, error) {
return a.BuildHttpClientWithModifyTls(func(config *tls.Config) {
config.Certificates = []tls.Certificate{
{
PrivateKey: a.PrivateKey,
Leaf: a.Certificate,
Certificate: [][]byte{a.Certificate.Raw},
},
}
})
}
func (a *AuthenticatorCert) Authenticate(controllerAddress *url.URL) (*rest_model.CurrentAPISessionDetail, error) {
httpClient, err := a.BuildHttpClient()
if err != nil {
return nil, err
}
path := rest_management_api_client.DefaultBasePath
if controllerAddress.Path != "" && controllerAddress.Path != "/" {
path = controllerAddress.Path
}
clientRuntime := openapiclient.NewWithClient(controllerAddress.Host, path, rest_management_api_client.DefaultSchemes, httpClient)
client := rest_management_api_client.New(clientRuntime, nil)
params := a.Params()
resp, err := client.Authentication.Authenticate(params)
if err != nil {
return nil, err
}
if resp.GetPayload() == nil {
return nil, fmt.Errorf("error, nil payload: %v", resp.Error())
}
return resp.GetPayload().Data, nil
}
func (a *AuthenticatorCert) Params() *authentication.AuthenticateParams {
return &authentication.AuthenticateParams{
Auth: &rest_model.Authenticate{
ConfigTypes: a.ConfigTypes,
EnvInfo: a.EnvInfo,
SdkInfo: a.SdkInfo,
},
Method: "cert",
Context: context.Background(),
}
}
type AuthenticatorAuthHeader struct {
AuthenticatorBase
Token string
}
func (a *AuthenticatorAuthHeader) AuthenticateRequest(request runtime.ClientRequest, registry strfmt.Registry) error {
return request.SetHeaderParam("authorization", a.Token)
}
func (a *AuthenticatorAuthHeader) BuildHttpClient() (*http.Client, error) {
return a.AuthenticatorBase.BuildHttpClientWithModifyTls(func(config *tls.Config) {
config.InsecureSkipVerify = true
})
}
func NewAuthenticatorAuthHeader(token string) *AuthenticatorAuthHeader {
return &AuthenticatorAuthHeader{
Token: token,
}
}
func (a *AuthenticatorAuthHeader) Params() *authentication.AuthenticateParams {
return &authentication.AuthenticateParams{
Auth: &rest_model.Authenticate{
ConfigTypes: a.ConfigTypes,
EnvInfo: a.EnvInfo,
SdkInfo: a.SdkInfo,
},
Method: "ext-jwt",
Context: context.Background(),
}
}
func (a *AuthenticatorAuthHeader) Authenticate(controllerAddress *url.URL) (*rest_model.CurrentAPISessionDetail, error) {
httpClient, err := a.BuildHttpClient()
if err != nil {
return nil, err
}
path := rest_management_api_client.DefaultBasePath
if controllerAddress.Path != "" && controllerAddress.Path != "/" {
path = controllerAddress.Path
}
clientRuntime := openapiclient.NewWithClient(controllerAddress.Host, path, rest_management_api_client.DefaultSchemes, httpClient)
clientRuntime.DefaultAuthentication = &HeaderAuth{
HeaderName: "Authorization",
HeaderValue: a.Token,
}
client := rest_management_api_client.New(clientRuntime, nil)
params := a.Params()
resp, err := client.Authentication.Authenticate(params)
if err != nil {
return nil, err
}
if resp.GetPayload() == nil {
return nil, fmt.Errorf("error, nil payload: %v", resp.Error())
}
return resp.GetPayload().Data, nil
}