Skip TLS verif for local address (#2323)
Since the console is talking locally to MinIO, skip the TLS verification if any. This will allow users to avoid defining the correct MINIO_SERVER_URL domain address, when TLS is enabled, is useful in a bare-metal setup.
This commit is contained in:
@@ -106,7 +106,7 @@ func getLoginDetailsResponse(params authApi.LoginDetailParams) (*models.LoginDet
|
|||||||
if oauth2.IsIDPEnabled() {
|
if oauth2.IsIDPEnabled() {
|
||||||
loginStrategy = models.LoginDetailsLoginStrategyRedirectDashServiceDashAccount
|
loginStrategy = models.LoginDetailsLoginStrategyRedirectDashServiceDashAccount
|
||||||
// initialize new oauth2 client
|
// initialize new oauth2 client
|
||||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient())
|
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, restapi.ErrorWithContext(ctx, err)
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams) (*models.L
|
|||||||
|
|
||||||
if oauth2.IsIDPEnabled() {
|
if oauth2.IsIDPEnabled() {
|
||||||
// initialize new oauth2 client
|
// initialize new oauth2 client
|
||||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient())
|
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, restapi.ErrorWithContext(ctx, err)
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func getOperatorSubnetLoginResponse(session *models.Principal, params operator_a
|
|||||||
if username == "" || password == "" {
|
if username == "" || password == "" {
|
||||||
return nil, restapi.ErrorWithContext(ctx, errors.New("empty credentials"))
|
return nil, restapi.ErrorWithContext(ctx, errors.New("empty credentials"))
|
||||||
}
|
}
|
||||||
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient()}
|
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient("")}
|
||||||
token, mfa, err := restapi.SubnetLogin(subnetHTTPClient, username, password)
|
token, mfa, err := restapi.SubnetLogin(subnetHTTPClient, username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, restapi.ErrorWithContext(ctx, err)
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
@@ -102,7 +102,7 @@ func getOperatorSubnetLoginResponse(session *models.Principal, params operator_a
|
|||||||
func getOperatorSubnetLoginMFAResponse(session *models.Principal, params operator_api.OperatorSubnetLoginMFAParams) (*models.OperatorSubnetLoginResponse, *models.Error) {
|
func getOperatorSubnetLoginMFAResponse(session *models.Principal, params operator_api.OperatorSubnetLoginMFAParams) (*models.OperatorSubnetLoginResponse, *models.Error) {
|
||||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient()}
|
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient("")}
|
||||||
res, err := subnet.LoginWithMFA(subnetHTTPClient, *params.Body.Username, *params.Body.MfaToken, *params.Body.Otp)
|
res, err := subnet.LoginWithMFA(subnetHTTPClient, *params.Body.Username, *params.Body.MfaToken, *params.Body.Otp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, restapi.ErrorWithContext(ctx, err)
|
return nil, restapi.ErrorWithContext(ctx, err)
|
||||||
@@ -115,7 +115,7 @@ func getOperatorSubnetLoginMFAResponse(session *models.Principal, params operato
|
|||||||
func getOperatorSubnetAPIKeyResponse(session *models.Principal, params operator_api.OperatorSubnetAPIKeyParams) (*models.OperatorSubnetAPIKey, *models.Error) {
|
func getOperatorSubnetAPIKeyResponse(session *models.Principal, params operator_api.OperatorSubnetAPIKeyParams) (*models.OperatorSubnetAPIKey, *models.Error) {
|
||||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient()}
|
subnetHTTPClient := &xhttp.Client{Client: restapi.GetConsoleHTTPClient("")}
|
||||||
token := params.HTTPRequest.URL.Query().Get("token")
|
token := params.HTTPRequest.URL.Query().Get("token")
|
||||||
apiKey, err := subnet.GetAPIKey(subnetHTTPClient, token)
|
apiKey, err := subnet.GetAPIKey(subnetHTTPClient, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -948,7 +948,7 @@ func getUsageWidgetsForDeployment(ctx context.Context, prometheusURL string, mAd
|
|||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalPrometheus(ctx context.Context, endpoint string, data interface{}) bool {
|
func unmarshalPrometheus(ctx context.Context, endpoint string, data interface{}) bool {
|
||||||
httpClnt := GetConsoleHTTPClient()
|
httpClnt := GetConsoleHTTPClient(endpoint)
|
||||||
resp, err := httpClnt.Get(endpoint)
|
resp, err := httpClnt.Get(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorWithContext(ctx, fmt.Errorf("Unable to fetch labels from prometheus (%s)", resp.Status))
|
ErrorWithContext(ctx, fmt.Errorf("Unable to fetch labels from prometheus (%s)", resp.Status))
|
||||||
@@ -975,7 +975,7 @@ func testPrometheusURL(ctx context.Context, url string) bool {
|
|||||||
ErrorWithContext(ctx, fmt.Errorf("error Building Request: (%v)", err))
|
ErrorWithContext(ctx, fmt.Errorf("error Building Request: (%v)", err))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
response, err := GetConsoleHTTPClient().Do(req)
|
response, err := GetConsoleHTTPClient(url).Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorWithContext(ctx, fmt.Errorf("default Prometheus URL not reachable, trying root testing: (%v)", err))
|
ErrorWithContext(ctx, fmt.Errorf("default Prometheus URL not reachable, trying root testing: (%v)", err))
|
||||||
newTestURL := req.URL.Scheme + "://" + req.URL.Host + "/-/healthy"
|
newTestURL := req.URL.Scheme + "://" + req.URL.Host + "/-/healthy"
|
||||||
@@ -984,7 +984,7 @@ func testPrometheusURL(ctx context.Context, url string) bool {
|
|||||||
ErrorWithContext(ctx, fmt.Errorf("error Building Root Request: (%v)", err))
|
ErrorWithContext(ctx, fmt.Errorf("error Building Root Request: (%v)", err))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
rootResponse, err := GetConsoleHTTPClient().Do(req2)
|
rootResponse, err := GetConsoleHTTPClient(newTestURL).Do(req2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// URL & Root tests didn't work. Prometheus not reachable
|
// URL & Root tests didn't work. Prometheus not reachable
|
||||||
ErrorWithContext(ctx, fmt.Errorf("root Prometheus URL not reachable: (%v)", err))
|
ErrorWithContext(ctx, fmt.Errorf("root Prometheus URL not reachable: (%v)", err))
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func SubnetRegisterWithAPIKey(ctx context.Context, minioClient MinioAdmin, apiKe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
registerResult, err := subnet.Register(GetConsoleHTTPClient(), serverInfo, apiKey, "", "")
|
registerResult, err := subnet.Register(GetConsoleHTTPClient(""), serverInfo, apiKey, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,7 @@ func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*xhttp.Cl
|
|||||||
}
|
}
|
||||||
subnetHTTPClient.Transport.(*http.Transport).Proxy = http.ProxyURL(subnetProxyURL)
|
subnetHTTPClient.Transport.(*http.Transport).Proxy = http.ProxyURL(subnetProxyURL)
|
||||||
} else {
|
} else {
|
||||||
subnetHTTPClient = GetConsoleHTTPClient()
|
subnetHTTPClient = GetConsoleHTTPClient("")
|
||||||
}
|
}
|
||||||
clientI := &xhttp.Client{
|
clientI := &xhttp.Client{
|
||||||
Client: subnetHTTPClient,
|
Client: subnetHTTPClient,
|
||||||
@@ -309,7 +309,7 @@ func GetSubnetInfoResponse(session *models.Principal, params subnetApi.SubnetInf
|
|||||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
client := &xhttp.Client{
|
client := &xhttp.Client{
|
||||||
Client: GetConsoleHTTPClient(),
|
Client: GetConsoleHTTPClient(""),
|
||||||
}
|
}
|
||||||
|
|
||||||
licenseInfo, err := subnet.ParseLicense(client, os.Getenv("CONSOLE_SUBNET_LICENSE"))
|
licenseInfo, err := subnet.ParseLicense(client, os.Getenv("CONSOLE_SUBNET_LICENSE"))
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/minio/console/models"
|
"github.com/minio/console/models"
|
||||||
@@ -479,7 +482,7 @@ func newAdminFromClaims(claims *models.Principal) (*madmin.AdminClient, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
adminClient.SetCustomTransport(GetConsoleHTTPClient().Transport)
|
adminClient.SetCustomTransport(GetConsoleHTTPClient(getMinIOServer()).Transport)
|
||||||
return adminClient, nil
|
return adminClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,17 +501,54 @@ func newAdminFromCreds(accessKey, secretKey, endpoint string, tlsEnabled bool) (
|
|||||||
|
|
||||||
// httpClient is a custom http client, this client should not be called directly and instead be
|
// httpClient is a custom http client, this client should not be called directly and instead be
|
||||||
// called using GetConsoleHTTPClient() to ensure is initialized and the certificates are loaded correctly
|
// called using GetConsoleHTTPClient() to ensure is initialized and the certificates are loaded correctly
|
||||||
var httpClient *http.Client
|
var httpClients = struct {
|
||||||
|
sync.Mutex
|
||||||
|
m map[string]*http.Client
|
||||||
|
}{
|
||||||
|
m: make(map[string]*http.Client),
|
||||||
|
}
|
||||||
|
|
||||||
// GetConsoleHTTPClient will initialize the console HTTP Client with fully populated custom TLS
|
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
|
||||||
// Transport that with loads certs at
|
// that points to the local machine - FQDN are not supported
|
||||||
// - ${HOME}/.console/certs/CAs
|
func isLocalIPEndpoint(addr string) bool {
|
||||||
// - ${HOME}/.minio/certs/CAs
|
u, err := url.Parse(addr)
|
||||||
func GetConsoleHTTPClient() *http.Client {
|
if err != nil {
|
||||||
if httpClient == nil {
|
return false
|
||||||
httpClient = PrepareConsoleHTTPClient(false)
|
|
||||||
}
|
}
|
||||||
return httpClient
|
return isLocalIPAddress(u.Hostname())
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
|
||||||
|
// that points to the local machine - FQDN are not supported
|
||||||
|
func isLocalIPAddress(ipAddr string) bool {
|
||||||
|
if ipAddr == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(ipAddr)
|
||||||
|
return ip != nil && ip.IsLoopback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConsoleHTTPClient caches different http clients depending on the target endpoint while taking
|
||||||
|
// in consideration CA certs stored in ${HOME}/.console/certs/CAs and ${HOME}/.minio/certs/CAs
|
||||||
|
// If the target endpoint points to a loopback device, skip the TLS verification.
|
||||||
|
func GetConsoleHTTPClient(address string) *http.Client {
|
||||||
|
u, err := url.Parse(address)
|
||||||
|
if err == nil {
|
||||||
|
address = u.Hostname()
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClients.Lock()
|
||||||
|
client, ok := httpClients.m[address]
|
||||||
|
httpClients.Unlock()
|
||||||
|
if ok {
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
client = PrepareConsoleHTTPClient(isLocalIPAddress(address))
|
||||||
|
httpClients.Lock()
|
||||||
|
httpClients.m[address] = client
|
||||||
|
httpClients.Unlock()
|
||||||
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac AdminClient) speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
|
func (ac AdminClient) speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
|
||||||
|
|||||||
@@ -330,12 +330,14 @@ func (s consoleSTSAssumeRole) IsExpired() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.Credentials, error) {
|
func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.Credentials, error) {
|
||||||
|
minioURL := getMinIOServer()
|
||||||
|
|
||||||
// Future authentication methods can be added under this switch statement
|
// Future authentication methods can be added under this switch statement
|
||||||
switch {
|
switch {
|
||||||
// LDAP authentication for Console
|
// LDAP authentication for Console
|
||||||
case ldap.GetLDAPEnabled():
|
case ldap.GetLDAPEnabled():
|
||||||
{
|
{
|
||||||
creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(), getMinIOServer(), accessKey, secretKey)
|
creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(minioURL), minioURL, accessKey, secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -354,8 +356,8 @@ func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.
|
|||||||
DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()),
|
DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()),
|
||||||
}
|
}
|
||||||
stsAssumeRole := &credentials.STSAssumeRole{
|
stsAssumeRole := &credentials.STSAssumeRole{
|
||||||
Client: GetConsoleHTTPClient(),
|
Client: GetConsoleHTTPClient(minioURL),
|
||||||
STSEndpoint: getMinIOServer(),
|
STSEndpoint: minioURL,
|
||||||
Options: opts,
|
Options: opts,
|
||||||
}
|
}
|
||||||
consoleSTSWrapper := consoleSTSAssumeRole{stsAssumeRole: stsAssumeRole}
|
consoleSTSWrapper := consoleSTSAssumeRole{stsAssumeRole: stsAssumeRole}
|
||||||
@@ -374,10 +376,12 @@ func getConsoleCredentialsFromSession(claims *models.Principal) *credentials.Cre
|
|||||||
// from the provided session token
|
// from the provided session token
|
||||||
func newMinioClient(claims *models.Principal) (*minio.Client, error) {
|
func newMinioClient(claims *models.Principal) (*minio.Client, error) {
|
||||||
creds := getConsoleCredentialsFromSession(claims)
|
creds := getConsoleCredentialsFromSession(claims)
|
||||||
minioClient, err := minio.New(getMinIOEndpoint(), &minio.Options{
|
endpoint := getMinIOEndpoint()
|
||||||
|
secure := getMinIOEndpointIsSecure()
|
||||||
|
minioClient, err := minio.New(endpoint, &minio.Options{
|
||||||
Creds: creds,
|
Creds: creds,
|
||||||
Secure: getMinIOEndpointIsSecure(),
|
Secure: secure,
|
||||||
Transport: GetConsoleHTTPClient().Transport,
|
Transport: GetConsoleHTTPClient(getMinIOServer()).Transport,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -414,7 +418,7 @@ func newS3BucketClient(claims *models.Principal, bucketName string, prefix strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("the provided endpoint is invalid")
|
return nil, fmt.Errorf("the provided endpoint is invalid")
|
||||||
}
|
}
|
||||||
s3Config := newS3Config(objectURL, claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken, false)
|
s3Config := newS3Config(objectURL, claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken)
|
||||||
client, pErr := mc.S3New(s3Config)
|
client, pErr := mc.S3New(s3Config)
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
return nil, pErr.Cause
|
return nil, pErr.Cause
|
||||||
@@ -438,7 +442,7 @@ func pathJoinFinalSlash(elem ...string) string {
|
|||||||
|
|
||||||
// newS3Config simply creates a new Config struct using the passed
|
// newS3Config simply creates a new Config struct using the passed
|
||||||
// parameters.
|
// parameters.
|
||||||
func newS3Config(endpoint, accessKey, secretKey, sessionToken string, insecure bool) *mc.Config {
|
func newS3Config(endpoint, accessKey, secretKey, sessionToken string) *mc.Config {
|
||||||
// We have a valid alias and hostConfig. We populate the/
|
// We have a valid alias and hostConfig. We populate the/
|
||||||
// consoleCredentials from the match found in the config file.
|
// consoleCredentials from the match found in the config file.
|
||||||
s3Config := new(mc.Config)
|
s3Config := new(mc.Config)
|
||||||
@@ -446,13 +450,16 @@ func newS3Config(endpoint, accessKey, secretKey, sessionToken string, insecure b
|
|||||||
s3Config.AppName = globalAppName
|
s3Config.AppName = globalAppName
|
||||||
s3Config.AppVersion = pkg.Version
|
s3Config.AppVersion = pkg.Version
|
||||||
s3Config.Debug = false
|
s3Config.Debug = false
|
||||||
s3Config.Insecure = insecure
|
|
||||||
|
|
||||||
s3Config.HostURL = endpoint
|
s3Config.HostURL = endpoint
|
||||||
s3Config.AccessKey = accessKey
|
s3Config.AccessKey = accessKey
|
||||||
s3Config.SecretKey = secretKey
|
s3Config.SecretKey = secretKey
|
||||||
s3Config.SessionToken = sessionToken
|
s3Config.SessionToken = sessionToken
|
||||||
s3Config.Signature = "S3v4"
|
s3Config.Signature = "S3v4"
|
||||||
|
|
||||||
|
insecure := isLocalIPEndpoint(endpoint)
|
||||||
|
|
||||||
|
s3Config.Insecure = insecure
|
||||||
s3Config.Transport = PrepareSTSClientTransport(insecure)
|
s3Config.Transport = PrepareSTSClientTransport(insecure)
|
||||||
|
|
||||||
return s3Config
|
return s3Config
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func getLogSearchResponse(session *models.Principal, params logApi.LogSearchPara
|
|||||||
}
|
}
|
||||||
|
|
||||||
func logSearch(endpoint string) (*models.LogSearchResponse, error) {
|
func logSearch(endpoint string) (*models.LogSearchResponse, error) {
|
||||||
httpClnt := GetConsoleHTTPClient()
|
httpClnt := GetConsoleHTTPClient(endpoint)
|
||||||
resp, err := httpClnt.Get(endpoint)
|
resp, err := httpClnt.Get(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("the Log Search API cannot be reached. Please review the URL and try again %v", err)
|
return nil, fmt.Errorf("the Log Search API cannot be reached. Please review the URL and try again %v", err)
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ func getLoginDetailsResponse(params authApi.LoginDetailParams, openIDProviders o
|
|||||||
loginStrategy = models.LoginDetailsLoginStrategyRedirect
|
loginStrategy = models.LoginDetailsLoginStrategyRedirect
|
||||||
for name, provider := range openIDProviders {
|
for name, provider := range openIDProviders {
|
||||||
// initialize new oauth2 client
|
// initialize new oauth2 client
|
||||||
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(name, nil, r, GetConsoleHTTPClient())
|
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(name, nil, r, GetConsoleHTTPClient(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrorWithContext(ctx, err, ErrOauth2Provider)
|
return nil, ErrorWithContext(ctx, err, ErrOauth2Provider)
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,7 @@ func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams, openIDProv
|
|||||||
lr := params.Body
|
lr := params.Body
|
||||||
if openIDProviders != nil {
|
if openIDProviders != nil {
|
||||||
// initialize new oauth2 client
|
// initialize new oauth2 client
|
||||||
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(idpName, nil, r, GetConsoleHTTPClient())
|
oauth2Client, err := openIDProviders.NewOauth2ProviderClient(idpName, nil, r, GetConsoleHTTPClient(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrorWithContext(ctx, err)
|
return nil, ErrorWithContext(ctx, err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user