// 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 . // 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 }