we need to make sure that we print in consistent format for the logs, remove the unnecessary logs everywhere used via `log.Print*`
176 lines
5.1 KiB
Go
176 lines
5.1 KiB
Go
// This file is part of MinIO Console Server
|
|
// Copyright (c) 2021 MinIO, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
package subnet
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/minio/console/pkg/http"
|
|
|
|
"github.com/minio/pkg/licverifier"
|
|
|
|
"github.com/minio/console/models"
|
|
"github.com/minio/madmin-go"
|
|
mc "github.com/minio/mc/cmd"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
func LoginWithMFA(client http.ClientI, username, mfaToken, otp string) (*LoginResp, error) {
|
|
mfaLoginReq := MfaReq{Username: username, OTP: otp, Token: mfaToken}
|
|
resp, err := subnetPostReq(client, subnetMFAURL(), mfaLoginReq, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
token := gjson.Get(resp, "token_info.access_token")
|
|
if token.Exists() {
|
|
return &LoginResp{AccessToken: token.String(), MfaToken: ""}, nil
|
|
}
|
|
return nil, errors.New("access token not found in response")
|
|
}
|
|
|
|
func Login(client http.ClientI, username, password string) (*LoginResp, error) {
|
|
loginReq := map[string]string{
|
|
"username": username,
|
|
"password": password,
|
|
}
|
|
respStr, err := subnetPostReq(client, subnetLoginURL(), loginReq, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mfaRequired := gjson.Get(respStr, "mfa_required").Bool()
|
|
if mfaRequired {
|
|
mfaToken := gjson.Get(respStr, "mfa_token").String()
|
|
if mfaToken == "" {
|
|
return nil, errors.New("missing mfa token")
|
|
}
|
|
return &LoginResp{AccessToken: "", MfaToken: mfaToken}, nil
|
|
}
|
|
token := gjson.Get(respStr, "token_info.access_token")
|
|
if token.Exists() {
|
|
return &LoginResp{AccessToken: token.String(), MfaToken: ""}, nil
|
|
}
|
|
return nil, errors.New("access token not found in response")
|
|
}
|
|
|
|
func GetOrganizations(client http.ClientI, token string) ([]*models.SubnetOrganization, error) {
|
|
headers := subnetAuthHeaders(token)
|
|
respStr, err := subnetGetReq(client, subnetOrgsURL(), headers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var organizations []*models.SubnetOrganization
|
|
if err = json.Unmarshal([]byte(respStr), &organizations); err != nil {
|
|
return nil, err
|
|
}
|
|
return organizations, nil
|
|
}
|
|
|
|
type LicenseTokenConfig struct {
|
|
APIKey string
|
|
License string
|
|
Proxy string
|
|
}
|
|
|
|
func Register(client http.ClientI, admInfo madmin.InfoMessage, apiKey, token, accountID string) (*LicenseTokenConfig, error) {
|
|
var headers map[string]string
|
|
regInfo := GetClusterRegInfo(admInfo)
|
|
regURL := subnetRegisterURL()
|
|
if apiKey != "" {
|
|
regURL += "?api_key=" + apiKey
|
|
} else {
|
|
if accountID == "" || token == "" {
|
|
return nil, errors.New("missing accountID or authentication token")
|
|
}
|
|
headers = subnetAuthHeaders(token)
|
|
regURL += "?aid=" + accountID
|
|
}
|
|
regToken, err := GenerateRegToken(regInfo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reqPayload := mc.ClusterRegistrationReq{Token: regToken}
|
|
resp, err := subnetPostReq(client, regURL, reqPayload, headers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
respJSON := gjson.Parse(resp)
|
|
subnetAPIKey := respJSON.Get("api_key").String()
|
|
licenseJwt := respJSON.Get("license").String()
|
|
|
|
if subnetAPIKey != "" || licenseJwt != "" {
|
|
return &LicenseTokenConfig{
|
|
APIKey: subnetAPIKey,
|
|
License: licenseJwt,
|
|
}, nil
|
|
}
|
|
return nil, errors.New("subnet api key not found")
|
|
}
|
|
|
|
const publicKey = "/downloads/license-pubkey.pem"
|
|
|
|
// downloadSubnetPublicKey will download the current subnet public key.
|
|
func downloadSubnetPublicKey(client http.ClientI) (string, error) {
|
|
// Get the public key directly from Subnet
|
|
url := fmt.Sprintf("%s%s", subnetBaseURL(), publicKey)
|
|
resp, err := client.Get(url)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
buf := new(bytes.Buffer)
|
|
_, err = buf.ReadFrom(resp.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), err
|
|
}
|
|
|
|
// ParseLicense parses the license with the bundle public key and return it's information
|
|
func ParseLicense(client http.ClientI, license string) (*licverifier.LicenseInfo, error) {
|
|
var publicKeys []string
|
|
|
|
subnetPubKey, err := downloadSubnetPublicKey(client)
|
|
if err != nil {
|
|
// there was an issue getting the subnet public key
|
|
// use hardcoded public keys instead
|
|
publicKeys = OfflinePublicKeys
|
|
} else {
|
|
publicKeys = append(publicKeys, subnetPubKey)
|
|
}
|
|
|
|
licenseInfo, err := GetLicenseInfoFromJWT(license, publicKeys)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return licenseInfo, nil
|
|
}
|
|
|
|
func GetAPIKey(client http.ClientI, token string) (string, error) {
|
|
resp, err := subnetGetReq(client, subnetAPIKeyURL(), subnetAuthHeaders(token))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
respJSON := gjson.Parse(resp)
|
|
apiKey := respJSON.Get("api_key").String()
|
|
return apiKey, nil
|
|
}
|