329 lines
9.0 KiB
Go
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
|
|
}
|