Subnet cluster registration (#1338)
- Removed old registration flow - Add support for new online and offline cluster registration flow - Support login accounts with mfa enabled - Registration screens Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
@@ -225,30 +225,5 @@ func startOperatorServer(ctx *cli.Context) error {
|
||||
|
||||
defer server.Shutdown()
|
||||
|
||||
// subnet license refresh process
|
||||
go func() {
|
||||
// start refreshing subnet license after 5 seconds..
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
failedAttempts := 0
|
||||
for {
|
||||
if err := operatorapi.RefreshLicense(); err != nil {
|
||||
operatorapi.LogError("Refreshing subnet license failed: %v", err)
|
||||
failedAttempts++
|
||||
// end license refresh after 3 consecutive failed attempts
|
||||
if failedAttempts >= 3 {
|
||||
return
|
||||
}
|
||||
// wait 5 minutes and retry again
|
||||
time.Sleep(time.Minute * 5)
|
||||
continue
|
||||
}
|
||||
// if license refreshed successfully reset the counter
|
||||
failedAttempts = 0
|
||||
// try to refresh license every 24 hrs
|
||||
time.Sleep(time.Hour * 24)
|
||||
}
|
||||
}()
|
||||
|
||||
return server.Serve()
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -30,6 +30,7 @@ require (
|
||||
github.com/rs/xid v1.3.0
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tidwall/gjson v1.10.2
|
||||
github.com/unrolled/secure v1.0.9
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||
@@ -118,7 +119,6 @@ require (
|
||||
github.com/rjeczalik/notify v0.9.2 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.21.8 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tidwall/gjson v1.10.2 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
|
||||
122
models/subnet_login_m_f_a_request.go
Normal file
122
models/subnet_login_m_f_a_request.go
Normal file
@@ -0,0 +1,122 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// SubnetLoginMFARequest subnet login m f a request
|
||||
//
|
||||
// swagger:model subnetLoginMFARequest
|
||||
type SubnetLoginMFARequest struct {
|
||||
|
||||
// mfa token
|
||||
// Required: true
|
||||
MfaToken *string `json:"mfa_token"`
|
||||
|
||||
// otp
|
||||
// Required: true
|
||||
Otp *string `json:"otp"`
|
||||
|
||||
// username
|
||||
// Required: true
|
||||
Username *string `json:"username"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet login m f a request
|
||||
func (m *SubnetLoginMFARequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateMfaToken(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateOtp(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateUsername(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginMFARequest) validateMfaToken(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("mfa_token", "body", m.MfaToken); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginMFARequest) validateOtp(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("otp", "body", m.Otp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginMFARequest) validateUsername(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("username", "body", m.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet login m f a request based on context it is used
|
||||
func (m *SubnetLoginMFARequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetLoginMFARequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetLoginMFARequest) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetLoginMFARequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
73
models/subnet_login_request.go
Normal file
73
models/subnet_login_request.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetLoginRequest subnet login request
|
||||
//
|
||||
// swagger:model subnetLoginRequest
|
||||
type SubnetLoginRequest struct {
|
||||
|
||||
// api key
|
||||
APIKey string `json:"apiKey,omitempty"`
|
||||
|
||||
// password
|
||||
Password string `json:"password,omitempty"`
|
||||
|
||||
// username
|
||||
Username string `json:"username,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet login request
|
||||
func (m *SubnetLoginRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet login request based on context it is used
|
||||
func (m *SubnetLoginRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetLoginRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetLoginRequest) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetLoginRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
142
models/subnet_login_response.go
Normal file
142
models/subnet_login_response.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetLoginResponse subnet login response
|
||||
//
|
||||
// swagger:model subnetLoginResponse
|
||||
type SubnetLoginResponse struct {
|
||||
|
||||
// access token
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
|
||||
// mfa token
|
||||
MfaToken string `json:"mfa_token,omitempty"`
|
||||
|
||||
// organizations
|
||||
Organizations []*SubnetOrganization `json:"organizations"`
|
||||
|
||||
// registered
|
||||
Registered bool `json:"registered,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet login response
|
||||
func (m *SubnetLoginResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateOrganizations(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginResponse) validateOrganizations(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Organizations) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Organizations); i++ {
|
||||
if swag.IsZero(m.Organizations[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Organizations[i] != nil {
|
||||
if err := m.Organizations[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this subnet login response based on the context it is used
|
||||
func (m *SubnetLoginResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateOrganizations(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginResponse) contextValidateOrganizations(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Organizations); i++ {
|
||||
|
||||
if m.Organizations[i] != nil {
|
||||
if err := m.Organizations[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetLoginResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetLoginResponse) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetLoginResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
82
models/subnet_organization.go
Normal file
82
models/subnet_organization.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetOrganization subnet organization
|
||||
//
|
||||
// swagger:model subnetOrganization
|
||||
type SubnetOrganization struct {
|
||||
|
||||
// account Id
|
||||
AccountID int64 `json:"accountId,omitempty"`
|
||||
|
||||
// company
|
||||
Company string `json:"company,omitempty"`
|
||||
|
||||
// is account owner
|
||||
IsAccountOwner bool `json:"isAccountOwner,omitempty"`
|
||||
|
||||
// short name
|
||||
ShortName string `json:"shortName,omitempty"`
|
||||
|
||||
// subscription status
|
||||
SubscriptionStatus string `json:"subscriptionStatus,omitempty"`
|
||||
|
||||
// user Id
|
||||
UserID int64 `json:"userId,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet organization
|
||||
func (m *SubnetOrganization) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet organization based on context it is used
|
||||
func (m *SubnetOrganization) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetOrganization) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetOrganization) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetOrganization
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
67
models/subnet_reg_token_response.go
Normal file
67
models/subnet_reg_token_response.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetRegTokenResponse subnet reg token response
|
||||
//
|
||||
// swagger:model SubnetRegTokenResponse
|
||||
type SubnetRegTokenResponse struct {
|
||||
|
||||
// reg token
|
||||
RegToken string `json:"regToken,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet reg token response
|
||||
func (m *SubnetRegTokenResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet reg token response based on context it is used
|
||||
func (m *SubnetRegTokenResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetRegTokenResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetRegTokenResponse) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetRegTokenResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
105
models/subnet_register_request.go
Normal file
105
models/subnet_register_request.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// SubnetRegisterRequest subnet register request
|
||||
//
|
||||
// swagger:model subnetRegisterRequest
|
||||
type SubnetRegisterRequest struct {
|
||||
|
||||
// account id
|
||||
// Required: true
|
||||
AccountID *string `json:"account_id"`
|
||||
|
||||
// token
|
||||
// Required: true
|
||||
Token *string `json:"token"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet register request
|
||||
func (m *SubnetRegisterRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateAccountID(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateToken(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetRegisterRequest) validateAccountID(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("account_id", "body", m.AccountID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetRegisterRequest) validateToken(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("token", "body", m.Token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet register request based on context it is used
|
||||
func (m *SubnetRegisterRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetRegisterRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetRegisterRequest) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetRegisterRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -94,8 +94,6 @@ func configureAPI(api *operations.OperatorAPI) http.Handler {
|
||||
registerVolumesHandlers(api)
|
||||
// Namespaces handlers
|
||||
registerNamespaceHandlers(api)
|
||||
// Subscription handlers
|
||||
registerSubscriptionHandlers(api)
|
||||
|
||||
api.PreServerShutdown = func() {}
|
||||
|
||||
|
||||
@@ -1,416 +0,0 @@
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/subnet"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
)
|
||||
|
||||
func registerSubscriptionHandlers(api *operations.OperatorAPI) {
|
||||
// Activate license subscription for a particular tenant
|
||||
api.OperatorAPISubscriptionActivateHandler = operator_api.SubscriptionActivateHandlerFunc(func(params operator_api.SubscriptionActivateParams, session *models.Principal) middleware.Responder {
|
||||
err := getSubscriptionActivateResponse(session, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionActivateDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionActivateNoContent()
|
||||
})
|
||||
// Refresh license for k8s cluster
|
||||
api.OperatorAPISubscriptionRefreshHandler = operator_api.SubscriptionRefreshHandlerFunc(func(params operator_api.SubscriptionRefreshParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionRefreshResponse(session)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionRefreshDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionRefreshOK().WithPayload(license)
|
||||
})
|
||||
// Validate subscription handler
|
||||
api.OperatorAPISubscriptionValidateHandler = operator_api.SubscriptionValidateHandlerFunc(func(params operator_api.SubscriptionValidateParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionValidateResponse(session, params.Body)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionValidateDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionValidateOK().WithPayload(license)
|
||||
})
|
||||
// Get subscription information handler
|
||||
api.OperatorAPISubscriptionInfoHandler = operator_api.SubscriptionInfoHandlerFunc(func(params operator_api.SubscriptionInfoParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionInfoResponse(session)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionInfoOK().WithPayload(license)
|
||||
})
|
||||
// Refresh license for k8s cluster
|
||||
api.OperatorAPISubscriptionRefreshHandler = operator_api.SubscriptionRefreshHandlerFunc(func(params operator_api.SubscriptionRefreshParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionRefreshResponse(session)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionRefreshDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionRefreshOK().WithPayload(license)
|
||||
})
|
||||
}
|
||||
|
||||
// retrieveLicense returns license from K8S secrets
|
||||
func retrieveLicense(ctx context.Context, sessionToken string) (string, error) {
|
||||
var license string
|
||||
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(sessionToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// Get cluster subscription license
|
||||
license, err = getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return license, nil
|
||||
}
|
||||
|
||||
// getSubscriptionLicense will retrieve stored license jwt from k8s secret
|
||||
func getSubscriptionLicense(ctx context.Context, clientSet K8sClientI, namespace, secretName string) (string, error) {
|
||||
// retrieve license stored in k8s
|
||||
licenseSecret, err := clientSet.getSecret(ctx, namespace, secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
license, ok := licenseSecret.Data[ConsoleSubnetLicense]
|
||||
if !ok {
|
||||
LogError("subnet secret does not contain a valid subnet license")
|
||||
return "", restapi.ErrorGeneric
|
||||
}
|
||||
return string(license), nil
|
||||
}
|
||||
|
||||
// addSubscriptionLicenseToTenant replace existing console tenant secret and adds the subnet license key
|
||||
func addSubscriptionLicenseToTenant(ctx context.Context, clientSet K8sClientI, opClient OperatorClientI, license string, tenant *miniov2.Tenant) error {
|
||||
// If Tenant has a configuration secret update the license there and MinIO pods doesn't need to get restarted
|
||||
if tenant.HasConfigurationSecret() {
|
||||
// Update the Tenant Configuration
|
||||
tenantConfigurationSecret, err := clientSet.getSecret(ctx, tenant.Namespace, tenant.Spec.Configuration.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := tenantConfigurationSecret.Data["config.env"]; ok {
|
||||
updatedTenantConfiguration := map[string]string{}
|
||||
tenantConfigurationMap := miniov2.ParseRawConfiguration(tenantConfigurationSecret.Data["config.env"])
|
||||
for key, val := range tenantConfigurationMap {
|
||||
updatedTenantConfiguration[key] = string(val)
|
||||
}
|
||||
updatedTenantConfiguration[MinIOSubnetLicense] = license
|
||||
// removing accesskey & secretkey that are added automatically by parsing function
|
||||
// and are not need it for the tenant itself
|
||||
delete(updatedTenantConfiguration, "accesskey")
|
||||
delete(updatedTenantConfiguration, "secretkey")
|
||||
tenantConfigurationSecret.Data = map[string][]byte{
|
||||
"config.env": []byte(GenerateTenantConfigurationFile(updatedTenantConfiguration)),
|
||||
}
|
||||
_, err = clientSet.updateSecret(ctx, tenant.Namespace, tenantConfigurationSecret, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("tenant configuration secret has wrong format")
|
||||
}
|
||||
} else {
|
||||
// If configuration file is not present set the license to the container env
|
||||
updatedTenant := tenant.DeepCopy()
|
||||
// reset container env vars
|
||||
updatedTenant.Spec.Env = []corev1.EnvVar{}
|
||||
var licenseIsSet bool
|
||||
for _, env := range tenant.GetEnvVars() {
|
||||
// check if license already exists and override
|
||||
if env.Name == MinIOSubnetLicense {
|
||||
updatedTenant.Spec.Env = append(updatedTenant.Spec.Env, corev1.EnvVar{
|
||||
Name: MinIOSubnetLicense,
|
||||
Value: license,
|
||||
})
|
||||
licenseIsSet = true
|
||||
} else {
|
||||
// copy existing container env variables
|
||||
updatedTenant.Spec.Env = append(updatedTenant.Spec.Env, env)
|
||||
}
|
||||
}
|
||||
// if license didnt exists append it
|
||||
if !licenseIsSet {
|
||||
updatedTenant.Spec.Env = append(updatedTenant.Spec.Env, corev1.EnvVar{
|
||||
Name: MinIOSubnetLicense,
|
||||
Value: license,
|
||||
})
|
||||
}
|
||||
// this will start MinIO pods rolling restart
|
||||
_, err := opClient.TenantUpdate(ctx, updatedTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSubscriptionRefreshResponse(session *models.Principal) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseKey, err := retrieveLicense(ctx, session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
newLicenseInfo, licenseRaw, err := subscriptionRefresh(client, licenseKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// save license key to k8s and restart all console pods
|
||||
if err = saveSubscriptionLicense(ctx, &k8sClient, licenseRaw); err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
}
|
||||
// update license for all existing tenants
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
// iterate over all tenants and update licenses
|
||||
tenants, err := opClient.TenantList(ctx, "", metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
for _, tenant := range tenants.Items {
|
||||
if err = addSubscriptionLicenseToTenant(ctx, &k8sClient, &opClient, licenseRaw, &tenant); err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return newLicenseInfo, nil
|
||||
}
|
||||
|
||||
// RefreshLicense will check current subnet license and try to renew it
|
||||
func RefreshLicense() error {
|
||||
// Get current license
|
||||
saK8SToken := getK8sSAToken()
|
||||
licenseKey, err := retrieveLicense(context.Background(), saK8SToken)
|
||||
if licenseKey == "" {
|
||||
return errors.New("no license present")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
// Attempt to refresh license
|
||||
_, refreshedLicenseKey, err := subscriptionRefresh(client, licenseKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if refreshedLicenseKey == "" {
|
||||
return errors.New("license expired, please open a support ticket at https://subnet.min.io/")
|
||||
}
|
||||
// store new license in memory for console ui
|
||||
LicenseKey = refreshedLicenseKey
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
clientSet, err := cluster.K8sClient(saK8SToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
return saveSubscriptionLicense(ctx, &k8sClient, refreshedLicenseKey)
|
||||
}
|
||||
|
||||
func subscriptionRefresh(httpClient *cluster.HTTPClient, license string) (*models.License, string, error) {
|
||||
licenseInfo, rawLicense, err := subnet.RefreshLicense(httpClient, license)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
AccountID: licenseInfo.AccountID,
|
||||
StorageCapacity: licenseInfo.StorageCapacity,
|
||||
Plan: licenseInfo.Plan,
|
||||
ExpiresAt: licenseInfo.ExpiresAt.String(),
|
||||
Organization: licenseInfo.Organization,
|
||||
}, rawLicense, nil
|
||||
}
|
||||
|
||||
// saveSubscriptionLicense will create or replace an existing subnet license secret in the k8s cluster
|
||||
func saveSubscriptionLicense(ctx context.Context, clientSet K8sClientI, license string) error {
|
||||
licenseSecret, err := clientSet.getSecret(ctx, cluster.Namespace, OperatorSubnetLicenseSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if k8serrors.IsNotFound(err) {
|
||||
// Save subnet license in k8s secrets
|
||||
licenseSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
ConsoleSubnetLicense: []byte(license),
|
||||
},
|
||||
}
|
||||
_, err = clientSet.createSecret(ctx, cluster.Namespace, licenseSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// update existing license
|
||||
licenseSecret.Data = map[string][]byte{
|
||||
ConsoleSubnetLicense: []byte(license),
|
||||
}
|
||||
_, err = clientSet.updateSecret(ctx, cluster.Namespace, licenseSecret, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// subscriptionValidate will validate the provided jwt license against the subnet public key
|
||||
func subscriptionValidate(client cluster.HTTPClientI, license, email, password string) (*models.License, string, error) {
|
||||
licenseInfo, rawLicense, err := subnet.ValidateLicense(client, license, email, password)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
AccountID: licenseInfo.AccountID,
|
||||
StorageCapacity: licenseInfo.StorageCapacity,
|
||||
Plan: licenseInfo.Plan,
|
||||
ExpiresAt: licenseInfo.ExpiresAt.String(),
|
||||
Organization: licenseInfo.Organization,
|
||||
}, rawLicense, nil
|
||||
}
|
||||
|
||||
// getSubscriptionValidateResponse
|
||||
func getSubscriptionValidateResponse(session *models.Principal, params *models.SubscriptionValidateRequest) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
// validate license key
|
||||
licenseInfo, license, err := subscriptionValidate(client, params.License, params.Email, params.Password)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidLicense, nil, err)
|
||||
}
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
// save license key to k8s
|
||||
if err = saveSubscriptionLicense(ctx, &k8sClient, license); err != nil {
|
||||
return nil, prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
return licenseInfo, nil
|
||||
}
|
||||
|
||||
func getSubscriptionActivateResponse(session *models.Principal, namespace, tenantName string) *models.Error {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
tenant, err := getTenant(ctx, &opClient, namespace, tenantName)
|
||||
if err != nil {
|
||||
return prepareError(err, errorGeneric)
|
||||
}
|
||||
// configure kubernetes client
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// Get cluster subscription license
|
||||
license, err := getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
if err != nil {
|
||||
return prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// add subscription license to existing console Tenant
|
||||
if err = addSubscriptionLicenseToTenant(ctx, &k8sClient, &opClient, license, tenant); err != nil {
|
||||
return prepareError(err, errorGeneric)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSubscriptionInfoResponse returns information about the current configured subnet license for Console
|
||||
func getSubscriptionInfoResponse(session *models.Principal) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
var licenseInfo *models.License
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseKey, err := retrieveLicense(ctx, session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
// validate license key and obtain license info
|
||||
licenseInfo, _, err = subscriptionValidate(client, licenseKey, "", "")
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
return licenseInfo, nil
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
v2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
|
||||
"errors"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func Test_addSubscriptionLicenseToTenant(t *testing.T) {
|
||||
k8sClient := k8sClientMock{}
|
||||
opClient := opClientMock{}
|
||||
tenant := &v2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Spec: v2.TenantSpec{},
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
clientSet K8sClientI
|
||||
opClient OperatorClientI
|
||||
license string
|
||||
tenant *v2.Tenant
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "success updating subscription for tenant with configuration file",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio-configuration",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config.env": []byte("export MINIO_SUBNET_LICENSE=\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I\""),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant because cannot get configuration file",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something wrong happened")
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant because configuration file has wrong format",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio-configuration",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"aaaaa": []byte("export MINIO_SUBNET_LICENSE=\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I\""),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant because cannot update configuration file",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio-configuration",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config.env": []byte("export MINIO_SUBNET_LICENSE=\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I\""),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something wrong happened")
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success updating subscription for tenant with env variable",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Env = []corev1.EnvVar{
|
||||
{
|
||||
Name: "MINIO_SUBNET_LICENSE",
|
||||
Value: "",
|
||||
ValueFrom: nil,
|
||||
},
|
||||
}
|
||||
opClientTenantUpdateMock = func(ctx context.Context, tenant *v2.Tenant, opts metav1.UpdateOptions) (*v2.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant with env variable because of update tenant error",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Env = []corev1.EnvVar{
|
||||
{
|
||||
Name: "MINIO_SUBNET_LICENSE",
|
||||
Value: "",
|
||||
ValueFrom: nil,
|
||||
},
|
||||
}
|
||||
opClientTenantUpdateMock = func(ctx context.Context, tenant *v2.Tenant, opts metav1.UpdateOptions) (*v2.Tenant, error) {
|
||||
return nil, errors.New("something wrong happened")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
if err := addSubscriptionLicenseToTenant(tt.args.ctx, tt.args.clientSet, tt.args.opClient, tt.args.license, tt.args.tenant); (err != nil) != tt.wantErr {
|
||||
t.Errorf("addSubscriptionLicenseToTenant() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_saveSubscriptionLicense(t *testing.T) {
|
||||
k8sClient := k8sClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
clientSet K8sClientI
|
||||
license string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error deleting existing secret",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
license: "1111111111",
|
||||
},
|
||||
mockFunc: func() {
|
||||
DeleteSecretMock = func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
|
||||
return nil
|
||||
}
|
||||
CreateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.CreateOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
if err := saveSubscriptionLicense(tt.args.ctx, tt.args.clientSet, tt.args.license); (err != nil) != tt.wantErr {
|
||||
t.Errorf("saveSubscriptionLicense() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getSubscriptionLicense(t *testing.T) {
|
||||
license := "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I"
|
||||
k8sClient := k8sClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
clientSet K8sClientI
|
||||
namespace string
|
||||
secretName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error because subscription license doesnt exists",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
namespace: "namespace",
|
||||
secretName: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error because license field doesnt exist in k8s secret",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
namespace: "namespace",
|
||||
secretName: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
imm := true
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
Immutable: &imm,
|
||||
Data: map[string][]byte{
|
||||
//ConsoleSubnetLicense: []byte(license),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "license obtained successfully",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
namespace: "namespace",
|
||||
secretName: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
wantErr: false,
|
||||
want: license,
|
||||
mockFunc: func() {
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
imm := true
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
Immutable: &imm,
|
||||
Data: map[string][]byte{
|
||||
ConsoleSubnetLicense: []byte(license),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := getSubscriptionLicense(tt.args.ctx, tt.args.clientSet, tt.args.namespace, tt.args.secretName)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getSubscriptionLicense() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("getSubscriptionLicense() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -558,18 +558,6 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
info.IdpOidcEnabled = oidcEnabled
|
||||
info.MinioTLS = minTenant.TLS()
|
||||
|
||||
// obtain current subnet license for tenant (if exists)
|
||||
if license, ok := tenantConfiguration[MinIOSubnetLicense]; ok {
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseInfo, _, _ := subscriptionValidate(client, string(license), "", "")
|
||||
// if licenseInfo is present attach it to the tenantInfo response
|
||||
if licenseInfo != nil {
|
||||
info.SubnetLicense = licenseInfo
|
||||
}
|
||||
}
|
||||
|
||||
// attach status information
|
||||
info.Status = &models.TenantStatus{
|
||||
HealthStatus: string(minTenant.Status.HealthStatus),
|
||||
@@ -1241,13 +1229,6 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
}
|
||||
}
|
||||
|
||||
// If Subnet License is present in k8s secrets, copy that to the MINIO_SUBNET_LICENSE env variable
|
||||
// of the console tenant
|
||||
license, _ := getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
if license != "" {
|
||||
tenantConfigurationENV[MinIOSubnetLicense] = license
|
||||
}
|
||||
|
||||
// add annotations
|
||||
var annotations map[string]string
|
||||
|
||||
|
||||
@@ -49,3 +49,15 @@ func GetLicenseInfoFromJWT(license string, publicKeys []string) (*licverifier.Li
|
||||
}
|
||||
return nil, errors.New("invalid license key")
|
||||
}
|
||||
|
||||
// MfaReq - JSON payload of the SUBNET mfa api
|
||||
type MfaReq struct {
|
||||
Username string `json:"username"`
|
||||
OTP string `json:"otp"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type LoginResp struct {
|
||||
AccessToken string
|
||||
MfaToken string
|
||||
}
|
||||
|
||||
@@ -29,9 +29,4 @@ JkO2PfyyAYEO/5dBlPh1Undu9WQl6J7B
|
||||
const (
|
||||
// Constants for subnet configuration
|
||||
ConsoleSubnetURL = "CONSOLE_SUBNET_URL"
|
||||
// Subnet endpoints
|
||||
publicKey = "/downloads/license-pubkey.pem"
|
||||
loginEndpoint = "/api/auth/login"
|
||||
refreshLicenseKeyEndpoint = "/api/auth/subscription/renew-license"
|
||||
licenseKeyEndpoint = "/api/auth/subscription/license-key"
|
||||
)
|
||||
|
||||
@@ -18,209 +18,94 @@
|
||||
package subnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go"
|
||||
mc "github.com/minio/mc/cmd"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/pkg/licverifier"
|
||||
)
|
||||
|
||||
// subnetLoginRequest body request for subnet login
|
||||
type subnetLoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
func LoginWithMFA(client cluster.HTTPClientI, 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")
|
||||
}
|
||||
|
||||
// tokenInfo
|
||||
type tokenInfo struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn float64 `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
// subnetLoginResponse body resonse from subnet after login
|
||||
type subnetLoginResponse struct {
|
||||
HasMembership bool `json:"has_memberships"`
|
||||
TokenInfo tokenInfo `json:"token_info"`
|
||||
}
|
||||
|
||||
// LicenseMetadata claims in subnet license
|
||||
type LicenseMetadata struct {
|
||||
Email string `json:"email"`
|
||||
Issuer string `json:"issuer"`
|
||||
TeamName string `json:"teamName"`
|
||||
ServiceType string `json:"serviceType"`
|
||||
RequestedAt string `json:"requestedAt"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
AccountID int64 `json:"accountId"`
|
||||
Capacity int64 `json:"capacity"`
|
||||
}
|
||||
|
||||
// subnetLicenseResponse body response returned by subnet license endpoint
|
||||
type subnetLicenseResponse struct {
|
||||
License string `json:"license"`
|
||||
Metadata LicenseMetadata `json:"metadata"`
|
||||
}
|
||||
|
||||
// subnetLoginRequest body request for subnet login
|
||||
type subnetRefreshRequest struct {
|
||||
License string `json:"license"`
|
||||
}
|
||||
|
||||
// getNewLicenseFromExistingLicense will perform license refresh based on the provided license key
|
||||
func getNewLicenseFromExistingLicense(client cluster.HTTPClientI, licenseKey string) (string, error) {
|
||||
request := subnetRefreshRequest{
|
||||
License: licenseKey,
|
||||
func Login(client cluster.HTTPClientI, username, password string) (*LoginResp, error) {
|
||||
loginReq := map[string]string{
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
// http body for login request
|
||||
payloadBytes, err := json.Marshal(request)
|
||||
respStr, err := subnetPostReq(client, subnetLoginURL(), loginReq, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
subnetURL := GetSubnetURL()
|
||||
url := fmt.Sprintf("%s%s", subnetURL, refreshLicenseKeyEndpoint)
|
||||
resp, err := client.Post(url, "application/json", bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetLicense := &subnetLicenseResponse{}
|
||||
// Parse subnet login response
|
||||
err = json.Unmarshal(bodyBytes, subnetLicense)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return subnetLicense.License, nil
|
||||
}
|
||||
|
||||
// getLicenseFromCredentials will perform authentication against subnet using
|
||||
// user provided credentials and return the current subnet license key
|
||||
func getLicenseFromCredentials(client cluster.HTTPClientI, username, password string) (string, error) {
|
||||
request := subnetLoginRequest{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
// http body for login request
|
||||
payloadBytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetURL := GetSubnetURL()
|
||||
url := fmt.Sprintf("%s%s", subnetURL, loginEndpoint)
|
||||
// Authenticate against subnet using email/password provided by user
|
||||
resp, err := client.Post(url, "application/json", bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetSession := &subnetLoginResponse{}
|
||||
// Parse subnet login response
|
||||
err = json.Unmarshal(bodyBytes, subnetSession)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Get license key using session token
|
||||
token := subnetSession.TokenInfo.AccessToken
|
||||
url = fmt.Sprintf("%s%s", subnetURL, licenseKeyEndpoint)
|
||||
req, err := http.NewRequest("POST", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return "", fmt.Errorf("subnet served returned status %d code", resp.StatusCode)
|
||||
}
|
||||
userLicense := &subnetLicenseResponse{}
|
||||
// Parse subnet license response
|
||||
err = json.Unmarshal(bodyBytes, userLicense)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return userLicense.License, nil
|
||||
}
|
||||
|
||||
// downloadSubnetPublicKey will download the current subnet public key.
|
||||
func downloadSubnetPublicKey(client cluster.HTTPClientI) (string, error) {
|
||||
// Get the public key directly from Subnet
|
||||
url := fmt.Sprintf("%s%s", GetSubnetURL(), 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
|
||||
}
|
||||
|
||||
// ValidateLicense will download the current subnet public key, if the public key its not available for license
|
||||
// verification then console will fall back to verification with hardcoded public keys
|
||||
func ValidateLicense(client cluster.HTTPClientI, licenseKey, email, password string) (licInfo *licverifier.LicenseInfo, license string, err error) {
|
||||
var publicKeys []string
|
||||
if email != "" && password != "" {
|
||||
// fetch subnet license key using user credentials
|
||||
license, err = getLicenseFromCredentials(client, email, password)
|
||||
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")
|
||||
}
|
||||
} else if licenseKey != "" {
|
||||
license = licenseKey
|
||||
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 cluster.HTTPClientI, token string) ([]*models.SubnetOrganization, error) {
|
||||
headers := subnetAuthHeaders(token)
|
||||
respStr, err := subnetGetReq(client, subnetOrgsURL(), headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var organizations []*models.SubnetOrganization
|
||||
err = json.Unmarshal([]byte(respStr), &organizations)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return organizations, nil
|
||||
}
|
||||
|
||||
func Register(client cluster.HTTPClientI, admInfo madmin.InfoMessage, apiKey, token, accountID string) (string, error) {
|
||||
var headers map[string]string
|
||||
regInfo := GetClusterRegInfo(admInfo)
|
||||
regURL := subnetRegisterURL()
|
||||
if apiKey != "" {
|
||||
regURL += "?api_key=" + apiKey
|
||||
} else {
|
||||
return nil, "", errors.New("invalid license")
|
||||
}
|
||||
subnetPubKey, err := downloadSubnetPublicKey(client)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
// there was an issue getting the subnet public key
|
||||
// use hardcoded public keys instead
|
||||
publicKeys = OfflinePublicKeys
|
||||
} else {
|
||||
publicKeys = append(publicKeys, subnetPubKey)
|
||||
}
|
||||
licInfo, err = GetLicenseInfoFromJWT(license, publicKeys)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return licInfo, license, nil
|
||||
}
|
||||
|
||||
func RefreshLicense(client cluster.HTTPClientI, licenseKey string) (licInfo *licverifier.LicenseInfo, license string, err error) {
|
||||
if licenseKey != "" {
|
||||
license, err = getNewLicenseFromExistingLicense(client, licenseKey)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
if accountID == "" || token == "" {
|
||||
return "", errors.New("missing accountID or authentication token")
|
||||
}
|
||||
licenseInfo, rawLicense, err := ValidateLicense(client, license, "", "")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return licenseInfo, rawLicense, nil
|
||||
headers = subnetAuthHeaders(token)
|
||||
regURL += "?aid=" + accountID
|
||||
}
|
||||
return nil, "", errors.New("invalid license")
|
||||
regToken, err := GenerateRegToken(regInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
reqPayload := mc.ClusterRegistrationReq{Token: regToken}
|
||||
resp, err := subnetPostReq(client, regURL, reqPayload, headers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetAPIKey := gjson.Parse(resp).Get("api_key").String()
|
||||
if subnetAPIKey != "" {
|
||||
return subnetAPIKey, nil
|
||||
}
|
||||
return "", errors.New("subnet api key not found")
|
||||
}
|
||||
|
||||
@@ -1,332 +0,0 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"errors"
|
||||
)
|
||||
|
||||
var HTTPGetMock func(url string) (resp *http.Response, err error)
|
||||
var HTTPPostMock func(url, contentType string, body io.Reader) (resp *http.Response, err error)
|
||||
var HTTPDoMock func(req *http.Request) (*http.Response, error)
|
||||
|
||||
type HTTPClientMock struct {
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
func (c *HTTPClientMock) Get(url string) (resp *http.Response, err error) {
|
||||
return HTTPGetMock(url)
|
||||
}
|
||||
|
||||
func (c *HTTPClientMock) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return HTTPPostMock(url, contentType, body)
|
||||
}
|
||||
|
||||
func (c *HTTPClientMock) Do(req *http.Request) (*http.Response, error) {
|
||||
return HTTPDoMock(req)
|
||||
}
|
||||
|
||||
func Test_getLicenseFromCredentials(t *testing.T) {
|
||||
// HTTP Client mock
|
||||
clientMock := HTTPClientMock{
|
||||
Client: &http.Client{},
|
||||
}
|
||||
type args struct {
|
||||
client HTTPClientMock
|
||||
username string
|
||||
password string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error when login against subnet",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "invalid",
|
||||
password: "invalid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error because of malformed subnet response",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "invalid",
|
||||
password: "invalid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("foo")))}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error when obtaining license from subnet",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "valid",
|
||||
password: "valid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
// returning test jwt token
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"has_memberships\":true,\"token_info\":{\"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik4wRXdOa1V5UXpORU1UUkNOekU0UmpSR1JVWkJSa1UxUmtZNE9EY3lOekZHTXpjNU1qZ3hNZyJ9.eyJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2dyb3VwcyI6W10sImh0dHBzOi8vaWQuc3VibmV0Lm1pbi5pby9jbGFpbXMvcm9sZXMiOltdLCJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2VtYWlsIjoibGVuaW4rYzFAbWluaW8uaW8iLCJpc3MiOiJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vIiwic3ViIjoiYXV0aDB8NWZjZWFlYTMyNTNhZjEwMDc3NDZkMDM0IiwiYXVkIjoiaHR0cHM6Ly9zdWJuZXQubWluLmlvL2FwaSIsImlhdCI6MTYwODQxNjE5NiwiZXhwIjoxNjExMDA4MTk2LCJhenAiOiI1WTA0eVZlejNiOFgxUFVzRHVqSmxuZXVuY3ExVjZxaiIsInNjb3BlIjoib2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCJ9.GC8DRLT0jUEteuBZBmyMXMswLSblCr_89Gu5NcVRUzKSYAaZ5VFW4UFgo1BpiC0sePuWJ0Vykitphx7znTfZfj5B3mZbOw3ejG6kxz7nm9DuYMmySJFYnwroZ9EP02vkW7-n_-YvEg8le1wXfkJ3lTUzO3aWddS4rfQRsZ2YJJUj61GiNyEK_QNP4PrYOuzLyD1wV75NejFqfcFoj7nRkT1K2BM0-89-_f2AFDGTjov6Ig6s1s-zLC9wxcYSmubNwpCJytZmQgPqIepOr065Y6OB4n0n0B5sXguuGuzb8VAkECrHhHPz8ta926fc0jC4XxVCNKdbV1_qC3-1yY7AJA\",\"expires_in\":2592000.0,\"token_type\":\"Bearer\"}}")))}, nil
|
||||
}
|
||||
HTTPDoMock = func(req *http.Request) (*http.Response, error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error when obtaining license from subnet because of malformed response",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "valid",
|
||||
password: "valid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
// returning test jwt token
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"has_memberships\":true,\"token_info\":{\"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik4wRXdOa1V5UXpORU1UUkNOekU0UmpSR1JVWkJSa1UxUmtZNE9EY3lOekZHTXpjNU1qZ3hNZyJ9.eyJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2dyb3VwcyI6W10sImh0dHBzOi8vaWQuc3VibmV0Lm1pbi5pby9jbGFpbXMvcm9sZXMiOltdLCJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2VtYWlsIjoibGVuaW4rYzFAbWluaW8uaW8iLCJpc3MiOiJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vIiwic3ViIjoiYXV0aDB8NWZjZWFlYTMyNTNhZjEwMDc3NDZkMDM0IiwiYXVkIjoiaHR0cHM6Ly9zdWJuZXQubWluLmlvL2FwaSIsImlhdCI6MTYwODQxNjE5NiwiZXhwIjoxNjExMDA4MTk2LCJhenAiOiI1WTA0eVZlejNiOFgxUFVzRHVqSmxuZXVuY3ExVjZxaiIsInNjb3BlIjoib2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCJ9.GC8DRLT0jUEteuBZBmyMXMswLSblCr_89Gu5NcVRUzKSYAaZ5VFW4UFgo1BpiC0sePuWJ0Vykitphx7znTfZfj5B3mZbOw3ejG6kxz7nm9DuYMmySJFYnwroZ9EP02vkW7-n_-YvEg8le1wXfkJ3lTUzO3aWddS4rfQRsZ2YJJUj61GiNyEK_QNP4PrYOuzLyD1wV75NejFqfcFoj7nRkT1K2BM0-89-_f2AFDGTjov6Ig6s1s-zLC9wxcYSmubNwpCJytZmQgPqIepOr065Y6OB4n0n0B5sXguuGuzb8VAkECrHhHPz8ta926fc0jC4XxVCNKdbV1_qC3-1yY7AJA\",\"expires_in\":2592000.0,\"token_type\":\"Bearer\"}}")))}, nil
|
||||
}
|
||||
HTTPDoMock = func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("foo")))}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "license obtained successfully",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "valid",
|
||||
password: "valid",
|
||||
},
|
||||
want: license,
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
// returning test jwt token
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"has_memberships\":true,\"token_info\":{\"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik4wRXdOa1V5UXpORU1UUkNOekU0UmpSR1JVWkJSa1UxUmtZNE9EY3lOekZHTXpjNU1qZ3hNZyJ9.eyJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2dyb3VwcyI6W10sImh0dHBzOi8vaWQuc3VibmV0Lm1pbi5pby9jbGFpbXMvcm9sZXMiOltdLCJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2VtYWlsIjoibGVuaW4rYzFAbWluaW8uaW8iLCJpc3MiOiJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vIiwic3ViIjoiYXV0aDB8NWZjZWFlYTMyNTNhZjEwMDc3NDZkMDM0IiwiYXVkIjoiaHR0cHM6Ly9zdWJuZXQubWluLmlvL2FwaSIsImlhdCI6MTYwODQxNjE5NiwiZXhwIjoxNjExMDA4MTk2LCJhenAiOiI1WTA0eVZlejNiOFgxUFVzRHVqSmxuZXVuY3ExVjZxaiIsInNjb3BlIjoib2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCJ9.GC8DRLT0jUEteuBZBmyMXMswLSblCr_89Gu5NcVRUzKSYAaZ5VFW4UFgo1BpiC0sePuWJ0Vykitphx7znTfZfj5B3mZbOw3ejG6kxz7nm9DuYMmySJFYnwroZ9EP02vkW7-n_-YvEg8le1wXfkJ3lTUzO3aWddS4rfQRsZ2YJJUj61GiNyEK_QNP4PrYOuzLyD1wV75NejFqfcFoj7nRkT1K2BM0-89-_f2AFDGTjov6Ig6s1s-zLC9wxcYSmubNwpCJytZmQgPqIepOr065Y6OB4n0n0B5sXguuGuzb8VAkECrHhHPz8ta926fc0jC4XxVCNKdbV1_qC3-1yY7AJA\",\"expires_in\":2592000.0,\"token_type\":\"Bearer\"}}"))),
|
||||
}, nil
|
||||
}
|
||||
HTTPDoMock = func(req *http.Request) (*http.Response, error) {
|
||||
// returning test jwt license
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"license\":\"" + license + "\",\"metadata\":{\"email\":\"lenin+c1@minio.io\",\"issuer\":\"subnet@minio.io\",\"accountId\":176,\"teamName\":\"console-customer\",\"serviceType\":\"STANDARD\",\"capacity\":25,\"requestedAt\":\"2020-12-19T22:23:31.609144732Z\",\"expiresAt\":\"2021-12-19T22:23:31.609144732Z\"}}"))),
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
got, err := getLicenseFromCredentials(&tt.args.client, tt.args.username, tt.args.password)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getLicenseFromCredentials() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("getLicenseFromCredentials() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_downloadSubnetPublicKey(t *testing.T) {
|
||||
// HTTP Client mock
|
||||
clientMock := HTTPClientMock{
|
||||
Client: &http.Client{},
|
||||
}
|
||||
type args struct {
|
||||
client HTTPClientMock
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error downloading public key",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
},
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "public key download successfully",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
},
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("foo")))}, nil
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
want: "foo",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
got, err := downloadSubnetPublicKey(&tt.args.client)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("downloadSubnetPublicKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("downloadSubnetPublicKey() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateLicense(t *testing.T) {
|
||||
// HTTP Client mock
|
||||
clientMock := HTTPClientMock{
|
||||
Client: &http.Client{},
|
||||
}
|
||||
type args struct {
|
||||
client HTTPClientMock
|
||||
licenseKey string
|
||||
email string
|
||||
password string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantLicense string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error because nor license nor user or password was provided",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: "",
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "error because could not get license from credentials",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: "",
|
||||
email: "email",
|
||||
password: "password",
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error because invalid license",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: "invalid license",
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(strings.NewReader(publicKeys[0]))}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "license validated successfully",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: license,
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(strings.NewReader(publicKeys[0]))}, nil
|
||||
}
|
||||
},
|
||||
wantLicense: license,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
_, gotLicense, err := ValidateLicense(&tt.args.client, tt.args.licenseKey, tt.args.email, tt.args.password)
|
||||
if !tt.wantErr {
|
||||
t.Skip() // FIXME: fix all success cases
|
||||
}
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateLicense() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotLicense != tt.wantLicense {
|
||||
t.Errorf("ValidateLicense() gotLicense = %v, want %v", gotLicense, tt.wantLicense)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
165
pkg/subnet/utils.go
Normal file
165
pkg/subnet/utils.go
Normal file
@@ -0,0 +1,165 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/madmin-go"
|
||||
mc "github.com/minio/mc/cmd"
|
||||
"github.com/minio/pkg/env"
|
||||
)
|
||||
|
||||
const (
|
||||
subnetRespBodyLimit = 1 << 20 // 1 MiB
|
||||
)
|
||||
|
||||
func subnetBaseURL() string {
|
||||
return env.Get(ConsoleSubnetURL, "https://subnet.min.io")
|
||||
}
|
||||
|
||||
func subnetRegisterURL() string {
|
||||
return subnetBaseURL() + "/api/cluster/register"
|
||||
}
|
||||
|
||||
func subnetLoginURL() string {
|
||||
return subnetBaseURL() + "/api/auth/login"
|
||||
}
|
||||
|
||||
func subnetOrgsURL() string {
|
||||
return subnetBaseURL() + "/api/auth/organizations"
|
||||
}
|
||||
|
||||
func subnetMFAURL() string {
|
||||
return subnetBaseURL() + "/api/auth/mfa-login"
|
||||
}
|
||||
|
||||
func GenerateRegToken(clusterRegInfo mc.ClusterRegistrationInfo) (string, error) {
|
||||
token, e := json.Marshal(clusterRegInfo)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(token), nil
|
||||
}
|
||||
|
||||
func subnetAuthHeaders(authToken string) map[string]string {
|
||||
return map[string]string{"Authorization": "Bearer " + authToken}
|
||||
}
|
||||
|
||||
func httpDo(client cluster.HTTPClientI, req *http.Request) (*http.Response, error) {
|
||||
//if globalSubnetProxyURL != nil {
|
||||
// client.Transport.(*http.Transport).Proxy = http.ProxyURL(globalSubnetProxyURL)
|
||||
//}
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
func subnetReqDo(client cluster.HTTPClientI, r *http.Request, headers map[string]string) (string, error) {
|
||||
for k, v := range headers {
|
||||
r.Header.Add(k, v)
|
||||
}
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
if len(ct) == 0 {
|
||||
r.Header.Add("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
resp, e := httpDo(client, r)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
respBytes, e := ioutil.ReadAll(io.LimitReader(resp.Body, subnetRespBodyLimit))
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
respStr := string(respBytes)
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return respStr, nil
|
||||
}
|
||||
return respStr, fmt.Errorf("Request failed with code %d and error: %s", resp.StatusCode, respStr)
|
||||
}
|
||||
|
||||
func subnetGetReq(client cluster.HTTPClientI, reqURL string, headers map[string]string) (string, error) {
|
||||
r, e := http.NewRequest(http.MethodGet, reqURL, nil)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
return subnetReqDo(client, r, headers)
|
||||
}
|
||||
|
||||
func subnetPostReq(client cluster.HTTPClientI, reqURL string, payload interface{}, headers map[string]string) (string, error) {
|
||||
body, e := json.Marshal(payload)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
r, e := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(body))
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
return subnetReqDo(client, r, headers)
|
||||
}
|
||||
|
||||
func GetClusterRegInfo(admInfo madmin.InfoMessage) mc.ClusterRegistrationInfo {
|
||||
noOfPools := 1
|
||||
noOfDrives := 0
|
||||
for _, srvr := range admInfo.Servers {
|
||||
if srvr.PoolNumber > noOfPools {
|
||||
noOfPools = srvr.PoolNumber
|
||||
}
|
||||
noOfDrives += len(srvr.Disks)
|
||||
}
|
||||
|
||||
totalSpace, usedSpace := getDriveSpaceInfo(admInfo)
|
||||
|
||||
return mc.ClusterRegistrationInfo{
|
||||
DeploymentID: admInfo.DeploymentID,
|
||||
ClusterName: admInfo.DeploymentID,
|
||||
UsedCapacity: admInfo.Usage.Size,
|
||||
Info: mc.ClusterInfo{
|
||||
MinioVersion: admInfo.Servers[0].Version,
|
||||
NoOfServerPools: noOfPools,
|
||||
NoOfServers: len(admInfo.Servers),
|
||||
NoOfDrives: noOfDrives,
|
||||
TotalDriveSpace: totalSpace,
|
||||
UsedDriveSpace: usedSpace,
|
||||
NoOfBuckets: admInfo.Buckets.Count,
|
||||
NoOfObjects: admInfo.Objects.Count,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getDriveSpaceInfo(admInfo madmin.InfoMessage) (uint64, uint64) {
|
||||
total := uint64(0)
|
||||
used := uint64(0)
|
||||
for _, srvr := range admInfo.Servers {
|
||||
for _, d := range srvr.Disks {
|
||||
total += d.TotalSpace
|
||||
used += d.UsedSpace
|
||||
}
|
||||
}
|
||||
return total, used
|
||||
}
|
||||
@@ -386,10 +386,17 @@ export const IAM_PAGES_PERMISSIONS = {
|
||||
IAM_SCOPES.ADMIN_SERVER_INFO,
|
||||
],
|
||||
[IAM_PAGES.TOOLS_SPEEDTEST]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
|
||||
[IAM_PAGES.REGISTER_SUPPORT]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
|
||||
[IAM_PAGES.REGISTER_SUPPORT]: [
|
||||
IAM_SCOPES.ADMIN_SERVER_INFO,
|
||||
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
|
||||
],
|
||||
[IAM_PAGES.CALL_HOME]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
|
||||
[IAM_PAGES.PROFILE]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
|
||||
[IAM_PAGES.HEALTH]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
|
||||
[IAM_PAGES.LICENSE]: [
|
||||
IAM_SCOPES.ADMIN_SERVER_INFO,
|
||||
IAM_SCOPES.ADMIN_CONFIG_UPDATE,
|
||||
],
|
||||
};
|
||||
|
||||
export const S3_ALL_RESOURCES = "arn:aws:s3:::*";
|
||||
|
||||
32
portal-ui/src/icons/OfflineRegistrationBackIcon.tsx
Normal file
32
portal-ui/src/icons/OfflineRegistrationBackIcon.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const OfflineRegistrationBackIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
viewBox="0 0 256 256"
|
||||
width="16.1"
|
||||
height="13.5"
|
||||
{...props}
|
||||
>
|
||||
<path data-name="Rect\xE1ngulo 1602" fill="none" d="M0 0h256v256H0z" />
|
||||
<g fill="#2781b0">
|
||||
<path
|
||||
data-name="Trazado 7242"
|
||||
d="m20.695 32.211 11.313-11.318 203.3 203.4-11.313 11.318Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7243"
|
||||
d="M19.371 106.631C6.694 118.186 0 133.962 0 152.26a61.725 61.725 0 0 0 20.253 46.312c12.578 11.424 29.547 17.714 47.778 17.714h114.108L55.275 89.429c-14.007 2.7-26.556 8.672-35.911 17.2Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7244"
|
||||
d="M238.286 203.889C249.875 194.662 256 180.961 256 164.264c0-30.939-24.23-47.692-48.894-51.341-3.258-20.595-12.03-38.216-25.568-51.249a76.817 76.817 0 0 0-53.589-21.459 73.336 73.336 0 0 0-41.553 12.506l151.47 151.492c.128-.107.285-.206.42-.313Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default OfflineRegistrationBackIcon;
|
||||
71
portal-ui/src/icons/OfflineRegistrationIcon.tsx
Normal file
71
portal-ui/src/icons/OfflineRegistrationIcon.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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/>.
|
||||
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const OfflineRegistrationIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
viewBox="0 0 256 256"
|
||||
width="26.9"
|
||||
height="26.9"
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="Offline-Registration_svg__a">
|
||||
<path
|
||||
data-name="Rect\xE1ngulo 1604"
|
||||
fill="none"
|
||||
d="M0 0h256v199.086H0z"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<path data-name="Rect\xE1ngulo 1602" fill="none" d="M0 0h256v256H0z" />
|
||||
<g data-name="Grupo 2526">
|
||||
<path
|
||||
data-name="Rect\xE1ngulo 1603"
|
||||
fill="#00142f"
|
||||
d="m19.235 39.602 10.497-10.49L218.26 217.77l-10.497 10.49z"
|
||||
/>
|
||||
<g data-name="Grupo 2525">
|
||||
<g
|
||||
data-name="Grupo 2524"
|
||||
clipPath="url(#Offline-Registration_svg__a)"
|
||||
fill="#00142f"
|
||||
transform="translate(0 29.146)"
|
||||
>
|
||||
<path
|
||||
data-name="Trazado 7273"
|
||||
d="m17.968 79.492.007.015a55.559 55.559 0 0 0-17.96 42.3 57.238 57.238 0 0 0 18.783 42.92 65.482 65.482 0 0 0 44.3 16.431h105.817L51.268 63.545a68.63 68.63 0 0 0-33.3 15.947"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7274"
|
||||
d="m222.825 99.169-.074.015h-.333l-.326-.03a22.226 22.226 0 0 1-9.028-2.8 4.017 4.017 0 0 0-.651-.3 3.823 3.823 0 0 0-.533.244 18.331 18.331 0 0 1-9.665 2.745 18.542 18.542 0 0 1-3.559-.348l-.955-.185-.866-.429a19.149 19.149 0 0 1-9.332-10 5.281 5.281 0 0 0-.3-.525 4.064 4.064 0 0 0-.474-.1 18.625 18.625 0 0 1-12.12-6.21l-.585-.666-.422-.792a19.8 19.8 0 0 1-1.843-13.35 6.256 6.256 0 0 0 .067-.9 4.811 4.811 0 0 0-.437-.511 19.647 19.647 0 0 1-6.209-12.306l-.089-.807.089-.8a19.526 19.526 0 0 1 5.21-11.211c-.644-.688-1.251-1.413-1.924-2.079a71.234 71.234 0 0 0-49.687-19.901 68.071 68.071 0 0 0-38.525 11.6l140.41 140.462c.118-.1.266-.192.392-.289v-.007a45.043 45.043 0 0 0 16.428-36.742c0-14.652-5.876-25.849-14.66-33.774"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7275"
|
||||
d="M255.963 51.509a15.953 15.953 0 0 0-5.121-10.049 8.872 8.872 0 0 1-1.48-1.991 9.8 9.8 0 0 1 .059-2.753 16.071 16.071 0 0 0-1.487-10.967l-.207-.385-.3-.333a14.943 14.943 0 0 0-9.82-5 8.149 8.149 0 0 1-2.316-.7 8.935 8.935 0 0 1-1.359-2.096 15.448 15.448 0 0 0-7.563-8.192l-.437-.215-.481-.1a14.62 14.62 0 0 0-10.633 1.965 8.262 8.262 0 0 1-2.405.888 8.3 8.3 0 0 1-2.401-.888 14.639 14.639 0 0 0-10.638-1.961l-.474.1-.444.215a15.505 15.505 0 0 0-7.563 8.192 8.821 8.821 0 0 1-1.369 2.109 8.149 8.149 0 0 1-2.316.7 14.96 14.96 0 0 0-9.82 5l-.3.333-.207.392a16.144 16.144 0 0 0-1.48 10.9 9.96 9.96 0 0 1 .059 2.775 9.2 9.2 0 0 1-1.487 2.013 15.9 15.9 0 0 0-5.103 10.048l-.044.4.044.4a15.934 15.934 0 0 0 5.106 10.057 9.031 9.031 0 0 1 1.487 1.983 9.861 9.861 0 0 1-.059 2.76 16.112 16.112 0 0 0 1.48 10.952l.207.392.3.333a14.96 14.96 0 0 0 9.82 5 8.149 8.149 0 0 1 2.316.7 9.082 9.082 0 0 1 1.376 2.109 15.446 15.446 0 0 0 7.563 8.162l.437.215.474.089a14.639 14.639 0 0 0 10.635-1.96 8.262 8.262 0 0 1 2.405-.888 8.533 8.533 0 0 1 2.472.925 18.627 18.627 0 0 0 7.526 2.331l.155.015h.185a9.794 9.794 0 0 0 3.16-.525l.229-.074.215-.111a15.421 15.421 0 0 0 7.57-8.185 9.2 9.2 0 0 1 1.376-2.1 8.03 8.03 0 0 1 2.309-.7 14.943 14.943 0 0 0 9.82-5l.3-.326.2-.392a15.981 15.981 0 0 0 1.487-10.982 10.04 10.04 0 0 1-.059-2.745 8.957 8.957 0 0 1 1.48-1.976 15.953 15.953 0 0 0 5.121-10.049l.044-.407Zm-47.751 15.655-15.387-16.081 5.454-5.683 9.933 10.353 18.342-19.108 5.458 5.706Z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default OfflineRegistrationIcon;
|
||||
88
portal-ui/src/icons/OnlineRegistrationBackIcon.tsx
Normal file
88
portal-ui/src/icons/OnlineRegistrationBackIcon.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const OnlineRegistrationBackIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
viewBox="0 0 256 256"
|
||||
width="16.52"
|
||||
height="12.86"
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="online-registration-back_svg__a">
|
||||
<path
|
||||
data-name="Rect\xE1ngulo 1600"
|
||||
fill="#2781b0"
|
||||
d="M0 0h256v199.269H0z"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<path data-name="Rect\xE1ngulo 1602" fill="none" d="M0 0h256v256H0z" />
|
||||
<g data-name="Grupo 2521">
|
||||
<g
|
||||
data-name="Grupo 2520"
|
||||
clipPath="url(#online-registration-back_svg__a)"
|
||||
fill="#2781b0"
|
||||
transform="translate(0 22.634)"
|
||||
>
|
||||
<path
|
||||
data-name="Trazado 7245"
|
||||
d="M110.325 123.433a78.259 78.259 0 0 0 .768 10.936h13.5v-21.871h-13.5a78.271 78.271 0 0 0-.768 10.936Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7246"
|
||||
d="M112.411 105.696h12.187V85.56c-4.871 2.382-9.583 9.676-12.187 20.141"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7247"
|
||||
d="M124.599 161.316v-20.141h-12.188c2.6 10.464 7.316 17.761 12.187 20.141"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7248"
|
||||
d="M162.4 105.7a38.951 38.951 0 0 0-18.91-17.748 52.941 52.941 0 0 1 7.113 17.748Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7249"
|
||||
d="M103.53 123.433a85.92 85.92 0 0 1 .711-10.937H90.854a38.2 38.2 0 0 0 0 21.873h13.384a86.293 86.293 0 0 1-.711-10.936"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7250"
|
||||
d="M112.5 87.95a38.954 38.954 0 0 0-18.909 17.748h11.8a53.038 53.038 0 0 1 7.113-17.748"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7251"
|
||||
d="M93.597 141.173a38.956 38.956 0 0 0 18.909 17.748 52.942 52.942 0 0 1-7.113-17.748Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7252"
|
||||
d="M151.757 112.499a84.331 84.331 0 0 1 0 21.873h13.385a38.182 38.182 0 0 0 0-21.873Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7253"
|
||||
d="M143.491 158.922a38.962 38.962 0 0 0 18.91-17.748h-11.8a52.968 52.968 0 0 1-7.113 17.748"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7254"
|
||||
d="M192.789 69.359c.12-1.539.177-2.98.177-4.393a64.966 64.966 0 0 0-129.932 0c0 1.413.058 2.854.177 4.393a64.967 64.967 0 0 0 1.754 129.91h126.069a64.967 64.967 0 0 0 1.754-129.91Zm-21.947 69.376a3.373 3.373 0 0 1-.2.561 45.463 45.463 0 0 1-85.276 0 3.126 3.126 0 0 1-.2-.561 44.686 44.686 0 0 1 0-30.59 3.233 3.233 0 0 1 .2-.561 45.463 45.463 0 0 1 85.277 0 3.128 3.128 0 0 1 .2.561 44.711 44.711 0 0 1 0 30.59"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7255"
|
||||
d="M131.398 141.173v20.141c4.871-2.38 9.583-9.677 12.187-20.141Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7256"
|
||||
d="M131.398 85.557v20.141h12.187c-2.6-10.464-7.316-17.758-12.187-20.141"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7257"
|
||||
d="M145.671 123.433a78.26 78.26 0 0 0-.769-10.937h-13.5v21.872h13.5a78.262 78.262 0 0 0 .769-10.936Z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default OnlineRegistrationBackIcon;
|
||||
109
portal-ui/src/icons/OnlineRegistrationIcon.tsx
Normal file
109
portal-ui/src/icons/OnlineRegistrationIcon.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
// 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/>.
|
||||
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const OnlineRegistrationIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
viewBox="0 0 256 256"
|
||||
width="32.12"
|
||||
height="25"
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="online-registration-icn_svg__a">
|
||||
<path
|
||||
data-name="Rect\xE1ngulo 1601"
|
||||
fill="none"
|
||||
d="M0 0h256v189.799H0z"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g data-name="Grupo 2523">
|
||||
<g
|
||||
data-name="Grupo 2522"
|
||||
transform="translate(0 32.999)"
|
||||
clipPath="url(#online-registration-icn_svg__a)"
|
||||
fill="#00142f"
|
||||
>
|
||||
<path
|
||||
data-name="Trazado 7258"
|
||||
d="M105.956 117.2a75.071 75.071 0 0 0 .763 10.469h12.926v-20.938h-12.926a75.072 75.072 0 0 0-.763 10.469"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7259"
|
||||
d="M119.607 100.222V80.94a29.091 29.091 0 0 0-11.667 19.282Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7260"
|
||||
d="M119.614 153.467h.008v-19.282h-11.675a29.062 29.062 0 0 0 11.667 19.282"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7261"
|
||||
d="M155.805 100.221a37.276 37.276 0 0 0-18.1-16.993 50.754 50.754 0 0 1 6.807 16.993Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7262"
|
||||
d="M99.417 117.2h.034a81.388 81.388 0 0 1 .679-10.469H87.323a36.628 36.628 0 0 0 0 20.938h12.773a82.781 82.781 0 0 1-.679-10.469"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7263"
|
||||
d="M108.039 83.229a37.31 37.31 0 0 0-18.099 16.992h11.293a50.754 50.754 0 0 1 6.806-16.993"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7264"
|
||||
d="M89.947 134.178a37.31 37.31 0 0 0 18.1 16.993 50.754 50.754 0 0 1-6.806-16.993Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7265"
|
||||
d="M145.603 106.731a80.807 80.807 0 0 1 0 20.938h12.811a36.5 36.5 0 0 0 0-20.938Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7266"
|
||||
d="M137.706 151.171a37.31 37.31 0 0 0 18.1-16.993h-11.294a50.754 50.754 0 0 1-6.806 16.993"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7267"
|
||||
d="m230.957 100.848-.443.221-.473.16a13.816 13.816 0 0 1-4.494.748v-.023h-.671a22.917 22.917 0 0 1-9.309-2.884 4.907 4.907 0 0 0-.671-.313q-.275.114-.549.252a18.913 18.913 0 0 1-13.636 2.472l-.992-.2-.9-.443a19.76 19.76 0 0 1-9.619-10.306 5.449 5.449 0 0 0-.305-.542 5.087 5.087 0 0 0-.488-.107 19.2 19.2 0 0 1-12.5-6.4l-.61-.687-.427-.809a20.457 20.457 0 0 1-1.908-13.735 5.126 5.126 0 0 0 .046-.969 5.773 5.773 0 0 0-.443-.526 20.249 20.249 0 0 1-6.379-12.682l-.092-.832.092-.832a20.268 20.268 0 0 1 6.394-12.682 4.831 4.831 0 0 0 .427-.549 5.1 5.1 0 0 0-.069-.961 20.376 20.376 0 0 1 .992-11.552A62.2 62.2 0 0 0 60.692 61.216c0 1.351.053 2.732.168 4.2a62.2 62.2 0 0 0 1.678 124.381h120.683a62.1 62.1 0 0 0 53.886-93.717 19.522 19.522 0 0 1-6.15 4.769m-67.064 30.957a3.466 3.466 0 0 1-.2.534 43.494 43.494 0 0 1-81.645 0 2.641 2.641 0 0 1-.2-.534 42.738 42.738 0 0 1 0-29.285 2.641 2.641 0 0 1 .2-.534 43.494 43.494 0 0 1 81.645 0 2.642 2.642 0 0 1 .2.534 42.827 42.827 0 0 1 0 29.285"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7268"
|
||||
d="M126.131 134.178v19.282a29.062 29.062 0 0 0 11.67-19.282Z"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7269"
|
||||
d="M126.131 80.94v19.282h11.67a29.091 29.091 0 0 0-11.67-19.282"
|
||||
/>
|
||||
<path data-name="Trazado 7270" d="M139.79 117.194Z" />
|
||||
<path
|
||||
data-name="Trazado 7271"
|
||||
d="M139.789 117.2a75.154 75.154 0 0 0-.763-10.469H126.1v20.93h12.926a74.96 74.96 0 0 0 .763-10.461"
|
||||
/>
|
||||
<path
|
||||
data-name="Trazado 7272"
|
||||
d="m251.907 61.322-.023-.008a12.677 12.677 0 0 0 4.113-8.02 12.677 12.677 0 0 0-4.113-8.02 12.75 12.75 0 0 1-2.564-3.632 13.77 13.77 0 0 1 0-4.746 12.755 12.755 0 0 0-1.167-8.783 11.643 11.643 0 0 0-7.714-3.884 12.384 12.384 0 0 1-4.3-1.442 13.206 13.206 0 0 1-2.564-3.739 12.157 12.157 0 0 0-5.99-6.532 11.279 11.279 0 0 0-8.279 1.526 12.67 12.67 0 0 1-4.419 1.528 12.67 12.67 0 0 1-4.426-1.526 11.279 11.279 0 0 0-8.279-1.526 12.2 12.2 0 0 0-5.975 6.524 13.175 13.175 0 0 1-2.587 3.762 12.346 12.346 0 0 1-4.281 1.435 11.643 11.643 0 0 0-7.714 3.884 12.757 12.757 0 0 0-1.152 8.737 14.158 14.158 0 0 1 0 4.746 13.16 13.16 0 0 1-2.587 3.67 12.632 12.632 0 0 0-4.105 8.027 12.6 12.6 0 0 0 4.113 8.012 13.135 13.135 0 0 1 2.587 3.632 14.2 14.2 0 0 1 0 4.754 12.8 12.8 0 0 0 1.16 8.783 11.643 11.643 0 0 0 7.714 3.884 12.346 12.346 0 0 1 4.281 1.435 13.246 13.246 0 0 1 2.587 3.754 12.165 12.165 0 0 0 5.975 6.493 11.285 11.285 0 0 0 8.279-1.526 12.67 12.67 0 0 1 4.43-1.527 12.67 12.67 0 0 1 4.426 1.526 15.413 15.413 0 0 0 6.219 1.923 6.5 6.5 0 0 0 2.053-.336 12.155 12.155 0 0 0 5.975-6.516 13.246 13.246 0 0 1 2.587-3.754 12.346 12.346 0 0 1 4.281-1.435 11.643 11.643 0 0 0 7.714-3.884 12.717 12.717 0 0 0 1.167-8.828 14.158 14.158 0 0 1 0-4.746 12.834 12.834 0 0 1 2.587-3.624m-41.363 7.706L194.689 52.44l5.631-5.883 10.233 10.683 18.931-19.679 5.631 5.883Z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<path data-name="Rect\xE1ngulo 1602" fill="none" d="M0 0h256v256H0z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default OnlineRegistrationIcon;
|
||||
44
portal-ui/src/icons/VerifiedIcon.tsx
Normal file
44
portal-ui/src/icons/VerifiedIcon.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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/>.
|
||||
|
||||
import * as React from "react";
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const VerifiedIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={20}
|
||||
height={20}
|
||||
className={`min-icon`}
|
||||
fill={"currentcolor"}
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="registration-icon_svg__a">
|
||||
<path data-name="Rect\xE1ngulo 1593" fill="#4ccb92" d="M0 0h20v20H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g data-name="Grupo 2469" clipPath="url(#registration-icon_svg__a)">
|
||||
<path
|
||||
data-name="Trazado 7117"
|
||||
d="M19.075 11.962a3.1 3.1 0 0 0 1.008-1.965 3.1 3.1 0 0 0-1.008-1.963 3.134 3.134 0 0 1-.633-.894 3.4 3.4 0 0 1 0-1.164 3.121 3.121 0 0 0-.286-2.154 2.856 2.856 0 0 0-1.892-.952 3.024 3.024 0 0 1-1.053-.353 3.232 3.232 0 0 1-.628-.917A2.982 2.982 0 0 0 13.118 0a2.77 2.77 0 0 0-2.029.383 3.079 3.079 0 0 1-1.085.368 3.079 3.079 0 0 1-1.085-.37A2.77 2.77 0 0 0 6.89-.002a2.99 2.99 0 0 0-1.465 1.599 3.236 3.236 0 0 1-.633.922 3.033 3.033 0 0 1-1.05.351 2.856 2.856 0 0 0-1.892.953 3.133 3.133 0 0 0-.284 2.142 3.448 3.448 0 0 1 0 1.164 3.216 3.216 0 0 1-.633.9A3.1 3.1 0 0 0-.075 9.996a3.1 3.1 0 0 0 1.008 1.965 3.246 3.246 0 0 1 .633.89 3.462 3.462 0 0 1 0 1.166 3.133 3.133 0 0 0 .284 2.154 2.856 2.856 0 0 0 1.892.952 3.033 3.033 0 0 1 1.05.351 3.234 3.234 0 0 1 .633.921 2.982 2.982 0 0 0 1.465 1.592 2.77 2.77 0 0 0 2.029-.383 3.076 3.076 0 0 1 1.085-.37 3.077 3.077 0 0 1 1.085.368 3.769 3.769 0 0 0 1.525.472 1.561 1.561 0 0 0 .5-.082 2.978 2.978 0 0 0 1.465-1.6 3.249 3.249 0 0 1 .633-.921 3.032 3.032 0 0 1 1.05-.351 2.856 2.856 0 0 0 1.892-.952 3.113 3.113 0 0 0 .284-2.157 3.445 3.445 0 0 1 0-1.164 3.16 3.16 0 0 1 .633-.889m-10.13 1.894-3.89-4.066 1.38-1.437 2.51 2.618 4.638-4.833 1.38 1.442Z"
|
||||
fill="#4ccb92"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default VerifiedIcon;
|
||||
@@ -144,12 +144,6 @@ export interface ChangeUserPasswordRequest {
|
||||
newSecretKey: string;
|
||||
}
|
||||
|
||||
export interface SubscriptionActivateRequest {
|
||||
license: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface IRemoteBucket {
|
||||
name: string;
|
||||
accessKey: string;
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
tooltipHelper,
|
||||
} from "../common/styleLibrary";
|
||||
import HelpIcon from "../../../../../icons/HelpIcon";
|
||||
import clsx from "clsx";
|
||||
|
||||
interface InputBoxProps {
|
||||
label: string;
|
||||
@@ -60,6 +61,7 @@ interface InputBoxProps {
|
||||
noLabelMinWidth?: boolean;
|
||||
pattern?: string;
|
||||
autoFocus?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -130,6 +132,7 @@ const InputBoxWrapper = ({
|
||||
pattern = "",
|
||||
autoFocus = false,
|
||||
classes,
|
||||
className = "",
|
||||
}: InputBoxProps) => {
|
||||
let inputProps: any = { "data-index": index, ...extraInputProps };
|
||||
|
||||
@@ -149,9 +152,10 @@ const InputBoxWrapper = ({
|
||||
<React.Fragment>
|
||||
<Grid
|
||||
container
|
||||
className={` ${
|
||||
className={clsx(
|
||||
className !== "" ? className : "",
|
||||
error !== "" ? classes.errorInField : classes.inputBoxContainer
|
||||
}`}
|
||||
)}
|
||||
>
|
||||
{label !== "" && (
|
||||
<InputLabel
|
||||
|
||||
@@ -14,17 +14,24 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React, { Fragment, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { LinearProgress, TextField } from "@mui/material";
|
||||
import { LinearProgress } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import { SubscriptionActivateRequest } from "../Buckets/types";
|
||||
import {
|
||||
SubnetLoginRequest,
|
||||
SubnetLoginResponse,
|
||||
SubnetLoginWithMFARequest,
|
||||
SubnetOrganization,
|
||||
SubnetRegisterRequest,
|
||||
SubnetRegTokenResponse,
|
||||
} from "./types";
|
||||
import { setModalErrorSnackMessage } from "../../../actions";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
@@ -33,23 +40,21 @@ import api from "../../../common/api";
|
||||
import PersonOutlineOutlinedIcon from "@mui/icons-material/PersonOutlineOutlined";
|
||||
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
|
||||
|
||||
import { formFieldStyles } from "../Common/FormComponents/common/styleLibrary";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import Link from "@mui/material/Link";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
subnetLicenseKey: {
|
||||
padding: "10px 10px 10px 0px",
|
||||
borderRight: "1px solid rgba(0, 0, 0, 0.12)",
|
||||
opacity: 0.5,
|
||||
"&:hover": { opacity: 1 },
|
||||
},
|
||||
subnetLoginForm: {
|
||||
padding: "10px 0px 10px 10px",
|
||||
opacity: 0.5,
|
||||
"&:hover": { opacity: 1 },
|
||||
},
|
||||
subnetLicenseKey: {},
|
||||
subnetLoginForm: {},
|
||||
licenseKeyField: {},
|
||||
pageTitle: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
registrationMode: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
button: {
|
||||
textTransform: "none",
|
||||
fontSize: 15,
|
||||
@@ -64,6 +69,7 @@ const styles = (theme: Theme) =>
|
||||
fontWeight: 700,
|
||||
marginLeft: 15,
|
||||
},
|
||||
...formFieldStyles,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
@@ -83,36 +89,288 @@ const ActivationModal = ({
|
||||
const [license, setLicense] = useState<string>("");
|
||||
const [subnetPassword, setSubnetPassword] = useState<string>("");
|
||||
const [subnetEmail, setSubnetEmail] = useState<string>("");
|
||||
const [subnetMFAToken, setSubnetMFAToken] = useState<string>("");
|
||||
const [subnetOTP, setSubnetOTP] = useState<string>("");
|
||||
const [subnetAccessToken, setSubnetAccessToken] = useState<string>("");
|
||||
const [selectedSubnetOrganisation, setSelectedSubnetOrganisation] =
|
||||
useState<string>("");
|
||||
const [subnetRegToken, setSubnetRegToken] = useState<string>("");
|
||||
const [subnetOrganizations, setSubnetOrganizations] = useState<
|
||||
SubnetOrganization[]
|
||||
>([]);
|
||||
const [onlineActivation, setOnlineActivation] = useState<boolean>(true);
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const activateProduct = () => {
|
||||
const clearForm = () => {
|
||||
setLicense("");
|
||||
setSubnetPassword("");
|
||||
setSubnetEmail("");
|
||||
setSubnetMFAToken("");
|
||||
setSubnetOTP("");
|
||||
};
|
||||
|
||||
const fetchSubnetRegToken = () => {
|
||||
if (loading || subnetRegToken) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
api
|
||||
.invoke("GET", "/api/v1/subnet/registration-token")
|
||||
.then((resp: SubnetRegTokenResponse) => {
|
||||
setLoading(false);
|
||||
if (resp && resp.regToken) {
|
||||
setSubnetRegToken(resp.regToken);
|
||||
}
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
};
|
||||
|
||||
const subnetRegister = () => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
let request: SubscriptionActivateRequest = {
|
||||
license: license,
|
||||
email: subnetEmail,
|
||||
password: subnetPassword,
|
||||
if (subnetAccessToken && selectedSubnetOrganisation) {
|
||||
const request: SubnetRegisterRequest = {
|
||||
token: subnetAccessToken,
|
||||
account_id: selectedSubnetOrganisation,
|
||||
};
|
||||
api
|
||||
.invoke("POST", "/api/v1/subnet/register", request)
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
clearForm();
|
||||
closeModal();
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const subnetLoginWithMFA = () => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const request: SubnetLoginWithMFARequest = {
|
||||
username: subnetEmail,
|
||||
otp: subnetOTP,
|
||||
mfa_token: subnetMFAToken,
|
||||
};
|
||||
api
|
||||
.invoke("POST", "/api/v1/subscription/validate", request)
|
||||
.then(() => {
|
||||
.invoke("POST", "/api/v1/subnet/login/mfa", request)
|
||||
.then((resp: SubnetLoginResponse) => {
|
||||
setLoading(false);
|
||||
setLicense("");
|
||||
setSubnetPassword("");
|
||||
setSubnetEmail("");
|
||||
closeModal();
|
||||
if (resp && resp.access_token && resp.organizations.length > 0) {
|
||||
setSubnetAccessToken(resp.access_token);
|
||||
setSubnetOrganizations(resp.organizations);
|
||||
setSelectedSubnetOrganisation(
|
||||
resp.organizations[0].accountId.toString()
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setLicense("");
|
||||
setSubnetPassword("");
|
||||
setSubnetEmail("");
|
||||
setSubnetOTP("");
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
};
|
||||
|
||||
const subnetLogin = () => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
let request: SubnetLoginRequest = {
|
||||
username: subnetEmail,
|
||||
password: subnetPassword,
|
||||
apiKey: license,
|
||||
};
|
||||
api
|
||||
.invoke("POST", "/api/v1/subnet/login", request)
|
||||
.then((resp: SubnetLoginResponse) => {
|
||||
setLoading(false);
|
||||
if (resp && resp.registered) {
|
||||
clearForm();
|
||||
closeModal();
|
||||
} else if (resp && resp.mfa_token) {
|
||||
setSubnetMFAToken(resp.mfa_token);
|
||||
} else if (resp && resp.access_token && resp.organizations.length > 0) {
|
||||
setSubnetAccessToken(resp.access_token);
|
||||
setSubnetOrganizations(resp.organizations);
|
||||
setSelectedSubnetOrganisation(
|
||||
resp.organizations[0].accountId.toString()
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
clearForm();
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
};
|
||||
|
||||
let clusterRegistrationForm: JSX.Element;
|
||||
if (subnetAccessToken && subnetOrganizations.length > 0) {
|
||||
clusterRegistrationForm = (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle2" gutterBottom component="div">
|
||||
Register MinIO cluster
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<RadioGroupSelector
|
||||
tooltip={"Please choose the organization for this cluster."}
|
||||
currentSelection={selectedSubnetOrganisation}
|
||||
id="subnet-organisation"
|
||||
name="subnet-organisation"
|
||||
label="Select an Organisation"
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
setSelectedSubnetOrganisation(e.target.value as string);
|
||||
}}
|
||||
selectorOptions={subnetOrganizations.map((organisation) => ({
|
||||
value: organisation.accountId.toString(),
|
||||
label: organisation.company,
|
||||
}))}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<Button
|
||||
className={classes.button}
|
||||
color="primary"
|
||||
onClick={() => subnetRegister()}
|
||||
disabled={loading || subnetAccessToken.trim().length === 0}
|
||||
variant="contained"
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
} else if (subnetMFAToken) {
|
||||
clusterRegistrationForm = (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle2" gutterBottom component="div">
|
||||
Two-Factor Authentication
|
||||
</Typography>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
Please enter the 6-digit verification code that was sent to your
|
||||
email address. This code will be valid for 5 minutes.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
overlayIcon={<LockOutlinedIcon />}
|
||||
id="subnet-otp"
|
||||
name="subnet-otp"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSubnetOTP(event.target.value);
|
||||
}}
|
||||
placeholder=""
|
||||
label=""
|
||||
type="text"
|
||||
value={subnetOTP}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => subnetLoginWithMFA()}
|
||||
disabled={
|
||||
loading ||
|
||||
subnetOTP.trim().length === 0 ||
|
||||
subnetMFAToken.trim().length === 0
|
||||
}
|
||||
variant="contained"
|
||||
>
|
||||
Verify
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
} else {
|
||||
clusterRegistrationForm = (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
overlayIcon={<PersonOutlineOutlinedIcon />}
|
||||
id="subnet-email"
|
||||
name="subnet-email"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSubnetEmail(event.target.value);
|
||||
}}
|
||||
placeholder="email"
|
||||
label=""
|
||||
type="text"
|
||||
value={subnetEmail}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
overlayIcon={<LockOutlinedIcon />}
|
||||
id="subnet-password"
|
||||
name="subnet-password"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSubnetPassword(event.target.value);
|
||||
}}
|
||||
placeholder="password"
|
||||
label=""
|
||||
type="password"
|
||||
value={subnetPassword}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => subnetLogin()}
|
||||
disabled={
|
||||
loading ||
|
||||
subnetEmail.trim().length === 0 ||
|
||||
subnetPassword.trim().length === 0
|
||||
}
|
||||
variant="contained"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
<Button
|
||||
className={classes.buttonSignup}
|
||||
color="primary"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.open("https://min.io/pricing", "_blank");
|
||||
}}
|
||||
variant="outlined"
|
||||
>
|
||||
Sign Up
|
||||
</Button>
|
||||
</Grid>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
<Link
|
||||
className={classes.registrationMode}
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
fetchSubnetRegToken();
|
||||
setOnlineActivation(false);
|
||||
}}
|
||||
>
|
||||
Offline Activation
|
||||
</Link>
|
||||
</Typography>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return open ? (
|
||||
<ModalWrapper
|
||||
title=""
|
||||
@@ -132,97 +390,83 @@ const ActivationModal = ({
|
||||
Activate SUBNET License
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item className={classes.subnetLicenseKey} xs={6}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
Enter your license key here
|
||||
</Typography>
|
||||
{onlineActivation ? (
|
||||
<Grid item className={classes.subnetLoginForm} xs={12}>
|
||||
<Grid container>{clusterRegistrationForm}</Grid>
|
||||
</Grid>
|
||||
<TextField
|
||||
id="license-key"
|
||||
placeholder=""
|
||||
multiline
|
||||
rows={3}
|
||||
value={license}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setLicense(event.target.value)
|
||||
}
|
||||
fullWidth
|
||||
className={classes.licenseKeyField}
|
||||
variant="outlined"
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => activateProduct()}
|
||||
disabled={loading || license.trim().length === 0}
|
||||
>
|
||||
Activate
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item className={classes.subnetLoginForm} xs={6}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
) : (
|
||||
<Grid item className={classes.subnetLicenseKey} xs={12}>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
Step 1: Copy the following registration token
|
||||
</Typography>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
overlayIcon={<PersonOutlineOutlinedIcon />}
|
||||
id="subnet-email"
|
||||
name="subnet-email"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSubnetEmail(event.target.value);
|
||||
}}
|
||||
placeholder="email"
|
||||
onChange={() => {}}
|
||||
id="registration-token"
|
||||
name="registration-token"
|
||||
placeholder=""
|
||||
label=""
|
||||
type="text"
|
||||
value={subnetEmail}
|
||||
value={subnetRegToken}
|
||||
disabled
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
overlayIcon={<LockOutlinedIcon />}
|
||||
id="subnet-password"
|
||||
name="subnet-password"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSubnetPassword(event.target.value);
|
||||
}}
|
||||
placeholder="password"
|
||||
label=""
|
||||
type="password"
|
||||
value={subnetPassword}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<Button
|
||||
className={classes.button}
|
||||
color="primary"
|
||||
onClick={() => activateProduct()}
|
||||
disabled={
|
||||
loading ||
|
||||
subnetEmail.trim().length === 0 ||
|
||||
subnetPassword.trim().length === 0
|
||||
}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => navigator.clipboard.writeText(subnetRegToken)}
|
||||
>
|
||||
Copy
|
||||
</Button>
|
||||
</Grid>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
Step 2: Use the previous token to register your cluster at:{" "}
|
||||
<Link
|
||||
color="inherit"
|
||||
href="https://subnet.min.io/cluster/register"
|
||||
target="_blank"
|
||||
>
|
||||
https://subnet.min.io/cluster/register
|
||||
</Link>
|
||||
</Typography>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
Step 3: Enter the API key generated by SUBNET
|
||||
</Typography>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
value={license}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setLicense(event.target.value)
|
||||
}
|
||||
id="api-key"
|
||||
name="api-key"
|
||||
placeholder=""
|
||||
label=""
|
||||
type="text"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => subnetLogin()}
|
||||
disabled={loading || license.trim().length === 0}
|
||||
>
|
||||
Activate
|
||||
</Button>
|
||||
<Button
|
||||
className={classes.buttonSignup}
|
||||
color="primary"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.open("https://min.io/pricing", "_blank");
|
||||
}}
|
||||
variant="outlined"
|
||||
>
|
||||
Sign Up
|
||||
</Button>
|
||||
</Grid>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
<Link
|
||||
className={classes.registrationMode}
|
||||
color="inherit"
|
||||
onClick={() => setOnlineActivation(true)}
|
||||
>
|
||||
Online Activation
|
||||
</Link>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
{loading && (
|
||||
<Grid item xs={12}>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
@@ -26,7 +26,7 @@ import Button from "@mui/material/Button";
|
||||
import Moment from "react-moment";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
||||
import { LicenseInfo } from "./types";
|
||||
import { SubnetInfo } from "./types";
|
||||
import { AppState } from "../../../store";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
@@ -37,6 +37,12 @@ import ActivationModal from "./ActivationModal";
|
||||
import LicenseModal from "./LicenseModal";
|
||||
import api from "../../../common/api";
|
||||
import { LicenseIcon } from "../../../icons";
|
||||
import { hasPermission } from "../../../common/SecureComponent/SecureComponent";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES,
|
||||
IAM_PAGES_PERMISSIONS,
|
||||
} from "../../../common/SecureComponent/permissions";
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
operatorMode: state.system.operatorMode,
|
||||
@@ -55,7 +61,6 @@ const styles = (theme: Theme) =>
|
||||
fontWeight: "bold",
|
||||
"& ul": {
|
||||
listStyleType: "square",
|
||||
// listStyleType: "none",
|
||||
"& li": {
|
||||
float: "left",
|
||||
fontSize: 14,
|
||||
@@ -301,36 +306,64 @@ interface ILicenseProps {
|
||||
}
|
||||
|
||||
const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
const [activateProductModal, setActivateProductModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [licenseModal, setLicenseModal] = useState<boolean>(false);
|
||||
|
||||
const [licenseInfo, setLicenseInfo] = useState<SubnetInfo>();
|
||||
const [currentPlanID, setCurrentPlanID] = useState<number>(0);
|
||||
const [loadingLicenseInfo, setLoadingLicenseInfo] = useState<boolean>(false);
|
||||
const [initialLicenseLoading, setInitialLicenseLoading] =
|
||||
useState<boolean>(true);
|
||||
const [loadingRefreshLicense, setLoadingRefreshLicense] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const getSubnetInfo = hasPermission(
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.LICENSE],
|
||||
true
|
||||
);
|
||||
|
||||
const closeModalAndFetchLicenseInfo = () => {
|
||||
setActivateProductModal(false);
|
||||
fetchLicenseInfo();
|
||||
};
|
||||
const fetchLicenseInfo = () => {
|
||||
setLoadingLicenseInfo(true);
|
||||
api
|
||||
.invoke("GET", `/api/v1/subscription/info`)
|
||||
.then((res: LicenseInfo) => {
|
||||
if (res) {
|
||||
if (res.plan === "STANDARD") {
|
||||
setCurrentPlanID(1);
|
||||
} else if (res.plan === "ENTERPRISE") {
|
||||
setCurrentPlanID(2);
|
||||
} else {
|
||||
setCurrentPlanID(1);
|
||||
|
||||
const fetchLicenseInfo = useCallback(() => {
|
||||
if (loadingLicenseInfo) {
|
||||
return;
|
||||
}
|
||||
if (getSubnetInfo) {
|
||||
setLoadingLicenseInfo(true);
|
||||
api
|
||||
.invoke("GET", `/api/v1/subnet/info`)
|
||||
.then((res: SubnetInfo) => {
|
||||
if (res) {
|
||||
if (res.plan === "STANDARD") {
|
||||
setCurrentPlanID(1);
|
||||
} else if (res.plan === "ENTERPRISE") {
|
||||
setCurrentPlanID(2);
|
||||
} else {
|
||||
setCurrentPlanID(1);
|
||||
}
|
||||
setLicenseInfo(res);
|
||||
}
|
||||
setLicenseInfo(res);
|
||||
}
|
||||
setLoadingLicenseInfo(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setLoadingLicenseInfo(false);
|
||||
});
|
||||
};
|
||||
setLoadingLicenseInfo(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setLoadingLicenseInfo(false);
|
||||
});
|
||||
} else {
|
||||
setLoadingLicenseInfo(false);
|
||||
}
|
||||
}, [loadingLicenseInfo, getSubnetInfo]);
|
||||
|
||||
const refreshLicense = () => {
|
||||
setLoadingRefreshLicense(true);
|
||||
api
|
||||
.invoke("POST", `/api/v1/subscription/refresh`, {})
|
||||
.then((res: LicenseInfo) => {
|
||||
.then((res: SubnetInfo) => {
|
||||
if (res) {
|
||||
if (res.plan === "STANDARD") {
|
||||
setCurrentPlanID(1);
|
||||
@@ -348,20 +381,12 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const [activateProductModal, setActivateProductModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [licenseModal, setLicenseModal] = useState<boolean>(false);
|
||||
|
||||
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo>();
|
||||
const [currentPlanID, setCurrentPlanID] = useState<number>(0);
|
||||
const [loadingLicenseInfo, setLoadingLicenseInfo] = useState<boolean>(true);
|
||||
const [loadingRefreshLicense, setLoadingRefreshLicense] =
|
||||
useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchLicenseInfo();
|
||||
}, []);
|
||||
if (initialLicenseLoading) {
|
||||
fetchLicenseInfo();
|
||||
setInitialLicenseLoading(false);
|
||||
}
|
||||
}, [fetchLicenseInfo, initialLicenseLoading, setInitialLicenseLoading]);
|
||||
|
||||
if (loadingLicenseInfo) {
|
||||
return (
|
||||
@@ -653,14 +678,8 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<b>Pricing</b>
|
||||
Are you already a customer?
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
The MinIO Subscription Network provides exclusive benefits across
|
||||
licensing, operations and support. See the pricing table below for
|
||||
more information.
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
@@ -669,12 +688,10 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
</Grid>
|
||||
<Grid item xs={12} className={clsx(classes.planItemsPadding)}>
|
||||
<Grid container>
|
||||
{operatorMode ? (
|
||||
<ActivationModal
|
||||
open={activateProductModal}
|
||||
closeModal={() => closeModalAndFetchLicenseInfo()}
|
||||
/>
|
||||
) : null}
|
||||
<ActivationModal
|
||||
open={activateProductModal}
|
||||
closeModal={() => closeModalAndFetchLicenseInfo()}
|
||||
/>
|
||||
<Grid container item xs={12} className={classes.tableContainer}>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={3} className={classes.detailsContainer} />
|
||||
@@ -892,8 +909,7 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
: button.text}
|
||||
</Button>
|
||||
</Grid>
|
||||
{operatorMode &&
|
||||
button.text === "Subscribe" &&
|
||||
{button.text === "Subscribe" &&
|
||||
!(
|
||||
licenseInfo &&
|
||||
licenseInfo.plan.toLowerCase() ===
|
||||
@@ -907,7 +923,7 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
setActivateProductModal(true);
|
||||
}}
|
||||
>
|
||||
Activate
|
||||
Register
|
||||
</button>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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/>.
|
||||
|
||||
export interface LicenseInfo {
|
||||
export interface SubnetInfo {
|
||||
account_id: number;
|
||||
email: string;
|
||||
expires_at: string;
|
||||
@@ -22,3 +22,40 @@ export interface LicenseInfo {
|
||||
storage_capacity: number;
|
||||
organization: string;
|
||||
}
|
||||
|
||||
export interface SubnetLoginRequest {
|
||||
username?: string;
|
||||
password?: string;
|
||||
apiKey?: string;
|
||||
}
|
||||
|
||||
export interface SubnetRegisterRequest {
|
||||
token: string;
|
||||
account_id: string;
|
||||
}
|
||||
|
||||
export interface SubnetOrganization {
|
||||
userId: number;
|
||||
accountId: number;
|
||||
subscriptionStatus: string;
|
||||
isAccountOwner: boolean;
|
||||
shortName: string;
|
||||
company: string;
|
||||
}
|
||||
|
||||
export interface SubnetLoginResponse {
|
||||
registered: boolean;
|
||||
mfa_token: string;
|
||||
access_token: string;
|
||||
organizations: SubnetOrganization[];
|
||||
}
|
||||
|
||||
export interface SubnetLoginWithMFARequest {
|
||||
username: string;
|
||||
otp: string;
|
||||
mfa_token: string;
|
||||
}
|
||||
|
||||
export interface SubnetRegTokenResponse {
|
||||
regToken: string;
|
||||
}
|
||||
|
||||
@@ -135,14 +135,14 @@ export const planButtons = [
|
||||
{
|
||||
id: 1,
|
||||
text: "Subscribe",
|
||||
text2: "Upgrade",
|
||||
text2: "Sign up",
|
||||
link: "https://subnet.min.io/subscription",
|
||||
plan: "standard",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
text: "Subscribe",
|
||||
text2: "Upgrade",
|
||||
text2: "Sign up",
|
||||
link: "https://subnet.min.io/subscription",
|
||||
plan: "enterprise",
|
||||
},
|
||||
|
||||
618
portal-ui/src/screens/Console/Support/Register.tsx
Normal file
618
portal-ui/src/screens/Console/Support/Register.tsx
Normal file
@@ -0,0 +1,618 @@
|
||||
// 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/>.
|
||||
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import {
|
||||
actionsTray,
|
||||
searchField,
|
||||
containerForHeader,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Button, Grid, Link, Typography } from "@mui/material";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
import { CopyIcon, UsersIcon } from "../../../icons";
|
||||
import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import OnlineRegistrationIcon from "../../../icons/OnlineRegistrationIcon";
|
||||
import OfflineRegistrationBackIcon from "../../../icons/OfflineRegistrationBackIcon";
|
||||
import OfflineRegistrationIcon from "../../../icons/OfflineRegistrationIcon";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import clsx from "clsx";
|
||||
import OnlineRegistrationBackIcon from "../../../icons/OnlineRegistrationBackIcon";
|
||||
import api from "../../../common/api";
|
||||
|
||||
import {
|
||||
SubnetInfo,
|
||||
SubnetLoginRequest,
|
||||
SubnetLoginResponse,
|
||||
SubnetLoginWithMFARequest,
|
||||
SubnetOrganization,
|
||||
SubnetRegisterRequest,
|
||||
SubnetRegTokenResponse,
|
||||
} from "../License/types";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
|
||||
import SelectWrapper from "../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import { hasPermission } from "../../../common/SecureComponent/SecureComponent";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES,
|
||||
IAM_PAGES_PERMISSIONS,
|
||||
} from "../../../common/SecureComponent/permissions";
|
||||
import VerifiedIcon from "../../../icons/VerifiedIcon";
|
||||
import { connect } from "react-redux";
|
||||
import { setErrorSnackMessage } from "../../../actions";
|
||||
|
||||
interface IRegister {
|
||||
classes: any;
|
||||
displayErrorMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
loading: {
|
||||
paddingTop: 8,
|
||||
paddingLeft: 40,
|
||||
},
|
||||
buttons: {
|
||||
justifyContent: "flex-start",
|
||||
gap: 20,
|
||||
},
|
||||
localMessage: {
|
||||
fontSize: 24,
|
||||
color: "#07193E",
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
marginBottom: 10,
|
||||
},
|
||||
registerActivationIcon: {
|
||||
color: theme.palette.primary.main,
|
||||
fontSize: 16,
|
||||
fontWeight: "bold",
|
||||
marginBottom: 20,
|
||||
"& .min-icon": {
|
||||
width: 32.12,
|
||||
height: 25,
|
||||
marginRight: 10,
|
||||
verticalAlign: "middle",
|
||||
},
|
||||
},
|
||||
registerActivationMode: {
|
||||
textAlign: "right",
|
||||
"& a": {
|
||||
cursor: "pointer",
|
||||
},
|
||||
},
|
||||
subnetDescription: {
|
||||
textAlign: "left",
|
||||
Font: "normal normal normal 14px/17px Lato",
|
||||
letterSpacing: 0,
|
||||
color: "#000000",
|
||||
"& span": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
subnetLoginInputBoxWrapper: {
|
||||
paddingRight: 20,
|
||||
},
|
||||
registeredStatus: {
|
||||
border: "1px solid #E2E2E2",
|
||||
padding: "24px 24px 24px 24px",
|
||||
borderRadius: 2,
|
||||
marginBottom: 25,
|
||||
backgroundColor: "#FBFAFA",
|
||||
"& .min-icon": {
|
||||
width: 20,
|
||||
height: 20,
|
||||
marginLeft: 48,
|
||||
marginRight: 13,
|
||||
verticalAlign: "middle",
|
||||
marginTop: -3,
|
||||
},
|
||||
"& span": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
offlineRegisterButton: {
|
||||
textAlign: "right",
|
||||
paddingRight: 20,
|
||||
},
|
||||
copyInputBox: {
|
||||
"& button": {
|
||||
border: "1px solid #5E5E5E",
|
||||
borderRadius: 2,
|
||||
},
|
||||
},
|
||||
link: {
|
||||
color: "#2781B0",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const Register = ({ classes, displayErrorMessage }: IRegister) => {
|
||||
const [license, setLicense] = useState<string>("");
|
||||
const [subnetPassword, setSubnetPassword] = useState<string>("");
|
||||
const [subnetEmail, setSubnetEmail] = useState<string>("");
|
||||
const [subnetMFAToken, setSubnetMFAToken] = useState<string>("");
|
||||
const [subnetOTP, setSubnetOTP] = useState<string>("");
|
||||
const [subnetAccessToken, setSubnetAccessToken] = useState<string>("");
|
||||
const [selectedSubnetOrganization, setSelectedSubnetOrganization] =
|
||||
useState<string>("");
|
||||
const [subnetRegToken, setSubnetRegToken] = useState<string>("");
|
||||
const [subnetOrganizations, setSubnetOrganizations] = useState<
|
||||
SubnetOrganization[]
|
||||
>([]);
|
||||
const [showPassword, setShowPassword] = useState<boolean>(false);
|
||||
const [onlineActivation, setOnlineActivation] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [loadingLicenseInfo, setLoadingLicenseInfo] = useState<boolean>(false);
|
||||
const [clusterRegistered, setClusterRegistered] = useState<boolean>(false);
|
||||
const [initialLicenseLoading, setInitialLicenseLoading] =
|
||||
useState<boolean>(true);
|
||||
|
||||
const clearForm = () => {
|
||||
setSubnetAccessToken("");
|
||||
setSelectedSubnetOrganization("");
|
||||
setSubnetRegToken("");
|
||||
setShowPassword(false);
|
||||
setOnlineActivation(true);
|
||||
setSubnetOrganizations([]);
|
||||
setLicense("");
|
||||
setSubnetPassword("");
|
||||
setSubnetEmail("");
|
||||
setSubnetMFAToken("");
|
||||
setSubnetOTP("");
|
||||
};
|
||||
|
||||
const getSubnetInfo = hasPermission(
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.LICENSE],
|
||||
true
|
||||
);
|
||||
|
||||
const fetchLicenseInfo = useCallback(() => {
|
||||
if (loadingLicenseInfo) {
|
||||
return;
|
||||
}
|
||||
if (getSubnetInfo) {
|
||||
setLoadingLicenseInfo(true);
|
||||
api
|
||||
.invoke("GET", `/api/v1/subnet/info`)
|
||||
.then((res: SubnetInfo) => {
|
||||
setClusterRegistered(true);
|
||||
setLoadingLicenseInfo(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
displayErrorMessage(err);
|
||||
setClusterRegistered(false);
|
||||
setLoadingLicenseInfo(false);
|
||||
});
|
||||
} else {
|
||||
setLoadingLicenseInfo(false);
|
||||
}
|
||||
}, [loadingLicenseInfo, getSubnetInfo, displayErrorMessage]);
|
||||
|
||||
const fetchSubnetRegToken = () => {
|
||||
if (loading || subnetRegToken) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
api
|
||||
.invoke("GET", "/api/v1/subnet/registration-token")
|
||||
.then((resp: SubnetRegTokenResponse) => {
|
||||
setLoading(false);
|
||||
if (resp && resp.regToken) {
|
||||
setSubnetRegToken(resp.regToken);
|
||||
}
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
displayErrorMessage(err);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const callRegister = (token: string, account_id: string) => {
|
||||
const request: SubnetRegisterRequest = {
|
||||
token: token,
|
||||
account_id: account_id,
|
||||
};
|
||||
api
|
||||
.invoke("POST", "/api/v1/subnet/register", request)
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
clearForm();
|
||||
fetchLicenseInfo();
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
displayErrorMessage(err);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
const subnetRegister = () => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
if (subnetAccessToken && selectedSubnetOrganization) {
|
||||
callRegister(subnetAccessToken, selectedSubnetOrganization);
|
||||
}
|
||||
};
|
||||
|
||||
const subnetLoginWithMFA = () => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const request: SubnetLoginWithMFARequest = {
|
||||
username: subnetEmail,
|
||||
otp: subnetOTP,
|
||||
mfa_token: subnetMFAToken,
|
||||
};
|
||||
api
|
||||
.invoke("POST", "/api/v1/subnet/login/mfa", request)
|
||||
.then((resp: SubnetLoginResponse) => {
|
||||
setLoading(false);
|
||||
if (resp && resp.access_token && resp.organizations.length > 0) {
|
||||
if (resp.organizations.length === 1) {
|
||||
callRegister(
|
||||
resp.access_token,
|
||||
resp.organizations[0].accountId.toString()
|
||||
);
|
||||
} else {
|
||||
setSubnetAccessToken(resp.access_token);
|
||||
setSubnetOrganizations(resp.organizations);
|
||||
setSelectedSubnetOrganization(
|
||||
resp.organizations[0].accountId.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
displayErrorMessage(err);
|
||||
setLoading(false);
|
||||
setSubnetOTP("");
|
||||
});
|
||||
};
|
||||
|
||||
const subnetLogin = () => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
let request: SubnetLoginRequest = {
|
||||
username: subnetEmail,
|
||||
password: subnetPassword,
|
||||
apiKey: license,
|
||||
};
|
||||
api
|
||||
.invoke("POST", "/api/v1/subnet/login", request)
|
||||
.then((resp: SubnetLoginResponse) => {
|
||||
setLoading(false);
|
||||
if (resp && resp.registered) {
|
||||
clearForm();
|
||||
fetchLicenseInfo();
|
||||
} else if (resp && resp.mfa_token) {
|
||||
setSubnetMFAToken(resp.mfa_token);
|
||||
} else if (resp && resp.access_token && resp.organizations.length > 0) {
|
||||
setSubnetAccessToken(resp.access_token);
|
||||
setSubnetOrganizations(resp.organizations);
|
||||
setSelectedSubnetOrganization(
|
||||
resp.organizations[0].accountId.toString()
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
displayErrorMessage(err);
|
||||
setLoading(false);
|
||||
clearForm();
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (initialLicenseLoading) {
|
||||
fetchLicenseInfo();
|
||||
setInitialLicenseLoading(false);
|
||||
}
|
||||
}, [fetchLicenseInfo, initialLicenseLoading, setInitialLicenseLoading]);
|
||||
|
||||
const title = onlineActivation ? (
|
||||
<Fragment>
|
||||
<OnlineRegistrationIcon />
|
||||
Online Activation SUBNET License
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<OfflineRegistrationIcon />
|
||||
Offline Activating SUBNET License
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
let clusterRegistrationForm: JSX.Element;
|
||||
|
||||
if (onlineActivation) {
|
||||
if (subnetAccessToken && subnetOrganizations.length > 0) {
|
||||
clusterRegistrationForm = (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.subnetDescription}>
|
||||
<Typography>Register MinIO cluster</Typography>
|
||||
</Grid>
|
||||
<br />
|
||||
<Grid item xs={4} className={classes.actionsTray}>
|
||||
<SelectWrapper
|
||||
id="subnet-organization"
|
||||
name="subnet-organization"
|
||||
onChange={(e) =>
|
||||
setSelectedSubnetOrganization(e.target.value as string)
|
||||
}
|
||||
label="Select an organization"
|
||||
value={selectedSubnetOrganization}
|
||||
options={subnetOrganizations.map((organization) => ({
|
||||
label: organization.company,
|
||||
value: organization.accountId.toString(),
|
||||
}))}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<Button
|
||||
className={classes.button}
|
||||
color="primary"
|
||||
onClick={() => subnetRegister()}
|
||||
disabled={loading || subnetAccessToken.trim().length === 0}
|
||||
variant="contained"
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
} else if (subnetMFAToken) {
|
||||
clusterRegistrationForm = (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.subnetDescription}>
|
||||
<Typography>Two-Factor Authentication</Typography>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
Please enter the 6-digit verification code that was sent to your
|
||||
email address. This code will be valid for 5 minutes.
|
||||
</Typography>
|
||||
</Grid>
|
||||
<br />
|
||||
<Grid item xs={3} className={clsx(classes.actionsTray)}>
|
||||
<InputBoxWrapper
|
||||
overlayIcon={<LockOutlinedIcon />}
|
||||
id="subnet-otp"
|
||||
name="subnet-otp"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setSubnetOTP(event.target.value)
|
||||
}
|
||||
placeholder=""
|
||||
label=""
|
||||
value={subnetOTP}
|
||||
/>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => subnetLoginWithMFA()}
|
||||
disabled={
|
||||
loading ||
|
||||
subnetOTP.trim().length === 0 ||
|
||||
subnetMFAToken.trim().length === 0
|
||||
}
|
||||
variant="contained"
|
||||
>
|
||||
Verify
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
} else {
|
||||
clusterRegistrationForm = (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.subnetDescription}>
|
||||
The MinIO Subscription Network (SUBNET for short) is a simple, yet
|
||||
powerful software stack that ensures commercial customers are
|
||||
getting the very best out of their MinIO instances. SUBNET is priced
|
||||
on capacity - just like cloud storage and comes in two
|
||||
configurations, Standard and Enterprise.
|
||||
</Grid>
|
||||
<br />
|
||||
<Grid item xs={12} className={classes.subnetDescription}>
|
||||
You can use your credentials from SUBNET to register.{" "}
|
||||
<Link
|
||||
className={classes.link}
|
||||
color="inherit"
|
||||
target="_blank"
|
||||
href="https://min.io/product/subnet"
|
||||
>
|
||||
Learn more about SUBNET
|
||||
</Link>
|
||||
</Grid>
|
||||
<br />
|
||||
<Grid item xs={12} className={clsx(classes.actionsTray)}>
|
||||
<InputBoxWrapper
|
||||
className={classes.subnetLoginInputBoxWrapper}
|
||||
id="subnet-email"
|
||||
name="subnet-email"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setSubnetEmail(event.target.value)
|
||||
}
|
||||
label="Email"
|
||||
value={subnetEmail}
|
||||
noLabelMinWidth
|
||||
overlayIcon={<UsersIcon />}
|
||||
/>
|
||||
<InputBoxWrapper
|
||||
className={classes.subnetLoginInputBoxWrapper}
|
||||
id="subnet-password"
|
||||
name="subnet-password"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setSubnetPassword(event.target.value)
|
||||
}
|
||||
label="Password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={subnetPassword}
|
||||
noLabelMinWidth
|
||||
overlayIcon={
|
||||
showPassword ? <VisibilityOffIcon /> : <RemoveRedEyeIcon />
|
||||
}
|
||||
overlayAction={() => setShowPassword(!showPassword)}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={
|
||||
loading ||
|
||||
subnetEmail.trim().length === 0 ||
|
||||
subnetPassword.trim().length === 0
|
||||
}
|
||||
onClick={() => subnetLogin()}
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
clusterRegistrationForm = (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.subnetDescription}>
|
||||
<span>Step 1:</span> Copy the following registration token
|
||||
</Grid>
|
||||
<Grid item xs={12} className={clsx(classes.actionsTray)}>
|
||||
<InputBoxWrapper
|
||||
className={clsx(
|
||||
classes.subnetLoginInputBoxWrapper,
|
||||
classes.copyInputBox
|
||||
)}
|
||||
id="registration-token"
|
||||
name="registration-token"
|
||||
placeholder=""
|
||||
label=""
|
||||
type="text"
|
||||
onChange={() => {}}
|
||||
value={subnetRegToken}
|
||||
disabled
|
||||
overlayIcon={<CopyIcon />}
|
||||
overlayAction={() => navigator.clipboard.writeText(subnetRegToken)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.subnetDescription}>
|
||||
<span>Step 2:</span> Use the previous token to register your cluster
|
||||
at:{" "}
|
||||
<Link
|
||||
className={classes.link}
|
||||
color="inherit"
|
||||
href="https://subnet.min.io/cluster/register"
|
||||
target="_blank"
|
||||
>
|
||||
https://subnet.min.io/cluster/register
|
||||
</Link>
|
||||
</Grid>
|
||||
<br />
|
||||
<Grid item xs={12} className={classes.subnetDescription}>
|
||||
<span>Step 3:</span> Enter the API key generated by SUBNET
|
||||
</Grid>
|
||||
<Grid item xs={12} className={clsx(classes.actionsTray)}>
|
||||
<InputBoxWrapper
|
||||
className={classes.subnetLoginInputBoxWrapper}
|
||||
value={license}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setLicense(event.target.value)
|
||||
}
|
||||
id="api-key"
|
||||
name="api-key"
|
||||
placeholder=""
|
||||
label=""
|
||||
type="text"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.offlineRegisterButton}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => subnetLogin()}
|
||||
disabled={loading || license.trim().length === 0}
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader label="Register" />
|
||||
<PageLayout>
|
||||
<Grid item xs={12} className={classes.boxy}>
|
||||
{clusterRegistered && (
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.registeredStatus}>
|
||||
Register Status:
|
||||
<VerifiedIcon />
|
||||
<span>Registered</span>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={6} className={classes.registerActivationIcon}>
|
||||
{title}
|
||||
</Grid>
|
||||
<Grid item xs={6} className={classes.registerActivationMode}>
|
||||
{onlineActivation ? (
|
||||
<Fragment>
|
||||
<OfflineRegistrationBackIcon />
|
||||
<Link
|
||||
className={classes.link}
|
||||
onClick={() => {
|
||||
fetchSubnetRegToken();
|
||||
setOnlineActivation(!onlineActivation);
|
||||
}}
|
||||
>
|
||||
Offline Activation
|
||||
</Link>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<OnlineRegistrationBackIcon />
|
||||
<Link
|
||||
className={classes.link}
|
||||
onClick={() => setOnlineActivation(!onlineActivation)}
|
||||
>
|
||||
Back to Online Activation
|
||||
</Link>
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{clusterRegistrationForm}
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, {
|
||||
displayErrorMessage: setErrorSnackMessage,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(Register));
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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/>.
|
||||
|
||||
import { LicenseInfo } from "../../License/types";
|
||||
import { SubnetInfo } from "../../License/types";
|
||||
import { IAffinityModel } from "../../../../common/types";
|
||||
import { NodeMaxAllocatableResources } from "../types";
|
||||
|
||||
@@ -114,7 +114,7 @@ export interface ITenant {
|
||||
capacity_usage?: number;
|
||||
// computed
|
||||
total_capacity: string;
|
||||
subnet_license: LicenseInfo;
|
||||
subnet_license: SubnetInfo;
|
||||
total_instances?: number;
|
||||
total_volumes?: number;
|
||||
}
|
||||
|
||||
@@ -26,15 +26,15 @@ import Moment from "react-moment";
|
||||
import { Link } from "react-router-dom";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import { ITenant } from "../ListTenants/types";
|
||||
import { LicenseInfo } from "../../License/types";
|
||||
import RBIconButton from "../../Buckets/BucketDetails/SummaryItems/RBIconButton";
|
||||
import { SubnetInfo } from "../../License/types";
|
||||
|
||||
interface ISubnetLicenseTenant {
|
||||
classes: any;
|
||||
tenant: ITenant | null;
|
||||
loadingActivateProduct: any;
|
||||
loadingLicenseInfo: boolean;
|
||||
licenseInfo: LicenseInfo | undefined;
|
||||
licenseInfo: SubnetInfo | undefined;
|
||||
activateProduct: any;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
tenantDetailsStyles,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { ITenant } from "../ListTenants/types";
|
||||
import { LicenseInfo } from "../../License/types";
|
||||
import { SubnetInfo } from "../../License/types";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import { AppState } from "../../../../store";
|
||||
import { setTenantDetailsLoad } from "../actions";
|
||||
@@ -56,7 +56,7 @@ const TenantLicense = ({
|
||||
loadingTenant,
|
||||
setTenantDetailsLoad,
|
||||
}: ITenantLicense) => {
|
||||
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo>();
|
||||
const [licenseInfo, setLicenseInfo] = useState<SubnetInfo>();
|
||||
const [loadingLicenseInfo, setLoadingLicenseInfo] = useState<boolean>(true);
|
||||
const [loadingActivateProduct, setLoadingActivateProduct] =
|
||||
useState<boolean>(false);
|
||||
@@ -87,7 +87,7 @@ const TenantLicense = ({
|
||||
if (loadingLicenseInfo) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/subscription/info`)
|
||||
.then((res: LicenseInfo) => {
|
||||
.then((res: SubnetInfo) => {
|
||||
setLicenseInfo(res);
|
||||
setLoadingLicenseInfo(false);
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ import { Route, Router, Switch } from "react-router-dom";
|
||||
import history from "../../../history";
|
||||
import NotFoundPage from "../../NotFoundPage";
|
||||
import ToolsList from "./ToolsPanel/ToolsList";
|
||||
import Register from "../Support/Register";
|
||||
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
import FeatureNotAvailablePage from "../Common/Components/FeatureNotAvailablePage";
|
||||
import { SupportMenuIcon } from "../../../icons/SidebarMenus/MenuIcons";
|
||||
@@ -28,20 +29,7 @@ const Tools = () => {
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path={IAM_PAGES.TOOLS} exact component={ToolsList} />
|
||||
<Route
|
||||
path={IAM_PAGES.REGISTER_SUPPORT}
|
||||
exact
|
||||
render={() => {
|
||||
return (
|
||||
<FeatureNotAvailablePage
|
||||
icon={<SupportMenuIcon />}
|
||||
pageHeaderText={"Register"}
|
||||
title={"Product registration"}
|
||||
message={<div>This feature is currently not available.</div>}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route path={IAM_PAGES.REGISTER_SUPPORT} exact component={Register} />
|
||||
<Route
|
||||
path={IAM_PAGES.CALL_HOME}
|
||||
exact
|
||||
|
||||
282
restapi/admin_subnet.go
Normal file
282
restapi/admin_subnet.go
Normal file
@@ -0,0 +1,282 @@
|
||||
// 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 restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/subnet"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
func registerSubnetHandlers(api *operations.ConsoleAPI) {
|
||||
// Get subnet login handler
|
||||
api.AdminAPISubnetLoginHandler = admin_api.SubnetLoginHandlerFunc(func(params admin_api.SubnetLoginParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetLoginResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewSubnetLoginDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSubnetLoginOK().WithPayload(resp)
|
||||
})
|
||||
// Get subnet login with MFA handler
|
||||
api.AdminAPISubnetLoginMFAHandler = admin_api.SubnetLoginMFAHandlerFunc(func(params admin_api.SubnetLoginMFAParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetLoginWithMFAResponse(params)
|
||||
if err != nil {
|
||||
return admin_api.NewSubnetLoginMFADefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSubnetLoginMFAOK().WithPayload(resp)
|
||||
})
|
||||
// Get subnet register
|
||||
api.AdminAPISubnetRegisterHandler = admin_api.SubnetRegisterHandlerFunc(func(params admin_api.SubnetRegisterParams, session *models.Principal) middleware.Responder {
|
||||
err := GetSubnetRegisterResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewSubnetRegisterDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSubnetRegisterOK()
|
||||
})
|
||||
// Get subnet info
|
||||
api.AdminAPISubnetInfoHandler = admin_api.SubnetInfoHandlerFunc(func(params admin_api.SubnetInfoParams, session *models.Principal) middleware.Responder {
|
||||
err := GetSubnetInfoResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewSubnetInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSubnetInfoOK()
|
||||
})
|
||||
// Get subnet registration token
|
||||
api.AdminAPISubnetRegTokenHandler = admin_api.SubnetRegTokenHandlerFunc(func(params admin_api.SubnetRegTokenParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetRegTokenResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewSubnetRegTokenDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSubnetRegTokenOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
func SubnetRegisterWithAPIKey(ctx context.Context, minioClient MinioAdmin, apiKey string) (bool, error) {
|
||||
serverInfo, err := minioClient.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
subnetAPIKey, err := subnet.Register(httpClient, serverInfo, apiKey, "", "")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
configStr := "subnet license= api_key=" + subnetAPIKey
|
||||
_, err = minioClient.setConfigKV(ctx, configStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// cluster registered correctly
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func SubnetLogin(client cluster.HTTPClientI, username, password string) (string, string, error) {
|
||||
tokens, err := subnet.Login(client, username, password)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if tokens.MfaToken != "" {
|
||||
// user needs to complete login flow using mfa
|
||||
return "", tokens.MfaToken, nil
|
||||
}
|
||||
if tokens.AccessToken != "" {
|
||||
// register token to minio
|
||||
return tokens.AccessToken, "", nil
|
||||
}
|
||||
return "", "", errors.New("something went wrong")
|
||||
}
|
||||
|
||||
func GetSubnetLoginResponse(session *models.Principal, params admin_api.SubnetLoginParams) (*models.SubnetLoginResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
httpClient := &cluster.HTTPClient{
|
||||
Client: GetConsoleHTTPClient(),
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
minioClient := AdminClient{Client: mAdmin}
|
||||
apiKey := params.Body.APIKey
|
||||
if apiKey != "" {
|
||||
registered, err := SubnetRegisterWithAPIKey(ctx, minioClient, apiKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
Registered: registered,
|
||||
Organizations: []*models.SubnetOrganization{},
|
||||
}, nil
|
||||
}
|
||||
username := params.Body.Username
|
||||
password := params.Body.Password
|
||||
if username != "" && password != "" {
|
||||
token, mfa, err := SubnetLogin(httpClient, username, password)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
MfaToken: mfa,
|
||||
AccessToken: token,
|
||||
Organizations: []*models.SubnetOrganization{},
|
||||
}, nil
|
||||
}
|
||||
return nil, prepareError(ErrorGeneric)
|
||||
}
|
||||
|
||||
type SubnetRegistration struct {
|
||||
AccessToken string
|
||||
MFAToken string
|
||||
Organizations []models.SubnetOrganization
|
||||
}
|
||||
|
||||
func SubnetLoginWithMFA(client cluster.HTTPClientI, username, mfaToken, otp string) (*models.SubnetLoginResponse, error) {
|
||||
tokens, err := subnet.LoginWithMFA(client, username, mfaToken, otp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokens.AccessToken != "" {
|
||||
organizations, errOrg := subnet.GetOrganizations(client, tokens.AccessToken)
|
||||
if errOrg != nil {
|
||||
return nil, errOrg
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
AccessToken: tokens.AccessToken,
|
||||
Organizations: organizations,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
|
||||
func GetSubnetLoginWithMFAResponse(params admin_api.SubnetLoginMFAParams) (*models.SubnetLoginResponse, *models.Error) {
|
||||
client := &cluster.HTTPClient{
|
||||
Client: GetConsoleHTTPClient(),
|
||||
}
|
||||
resp, err := SubnetLoginWithMFA(client, *params.Body.Username, *params.Body.MfaToken, *params.Body.Otp)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func GetSubnetKeyFromMinIOConfig(ctx context.Context, minioClient MinioAdmin, key string) (string, error) {
|
||||
sh, err := minioClient.helpConfigKV(ctx, "subnet", "", false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf, err := minioClient.getConfigKV(ctx, "subnet")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tgt, err := madmin.ParseSubSysTarget(buf, sh)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, kv := range tgt.KVS {
|
||||
if kv.Key == key {
|
||||
return kv.Value, nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("")
|
||||
}
|
||||
|
||||
func GetSubnetRegister(ctx context.Context, minioClient MinioAdmin, httpClient cluster.HTTPClientI, params admin_api.SubnetRegisterParams) error {
|
||||
serverInfo, err := minioClient.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subnetAPIKey, err := subnet.Register(httpClient, serverInfo, "", *params.Body.Token, *params.Body.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := "subnet license= api_key=" + subnetAPIKey
|
||||
_, err = minioClient.setConfigKV(ctx, configStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSubnetRegisterResponse(session *models.Principal, params admin_api.SubnetRegisterParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
client := &cluster.HTTPClient{
|
||||
Client: GetConsoleHTTPClient(),
|
||||
}
|
||||
err = GetSubnetRegister(ctx, adminClient, client, params)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSubnetInfoResponse(session *models.Principal) *models.Error {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
apiKey, err := GetSubnetKeyFromMinIOConfig(ctx, adminClient, "api_key")
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
if apiKey == "" {
|
||||
return prepareError(errLicenseNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSubnetRegToken(ctx context.Context, minioClient MinioAdmin) (string, error) {
|
||||
serverInfo, err := minioClient.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
regInfo := subnet.GetClusterRegInfo(serverInfo)
|
||||
regToken, err := subnet.GenerateRegToken(regInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return regToken, nil
|
||||
}
|
||||
|
||||
func GetSubnetRegTokenResponse(session *models.Principal) (*models.SubnetRegTokenResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
token, err := GetSubnetRegToken(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return &models.SubnetRegTokenResponse{
|
||||
RegToken: token,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// 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 restapi
|
||||
|
||||
import (
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/pkg/subnet"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
)
|
||||
|
||||
func registerSubscriptionHandlers(api *operations.ConsoleAPI) {
|
||||
// Get subscription information handler
|
||||
api.AdminAPISubscriptionInfoHandler = admin_api.SubscriptionInfoHandlerFunc(func(params admin_api.SubscriptionInfoParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionInfoResponse()
|
||||
if err != nil {
|
||||
return admin_api.NewSubscriptionInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSubscriptionInfoOK().WithPayload(license)
|
||||
})
|
||||
}
|
||||
|
||||
// retrieveLicense returns license from K8S secrets (If console is deployed in operator mode) or from
|
||||
// the configured CONSOLE_SUBNET_LICENSE environment variable
|
||||
func retrieveLicense() string {
|
||||
// If Console is running in Tenant Admin mode retrieve license from env variable
|
||||
license := GetSubnetLicense()
|
||||
return license
|
||||
}
|
||||
|
||||
// subscriptionValidate will validate the provided jwt license against the subnet public key
|
||||
func subscriptionValidate(client cluster.HTTPClientI, license, email, password string) (*models.License, string, error) {
|
||||
licenseInfo, rawLicense, err := subnet.ValidateLicense(client, license, email, password)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
AccountID: licenseInfo.AccountID,
|
||||
StorageCapacity: licenseInfo.StorageCapacity,
|
||||
Plan: licenseInfo.Plan,
|
||||
ExpiresAt: licenseInfo.ExpiresAt.String(),
|
||||
Organization: licenseInfo.Organization,
|
||||
}, rawLicense, nil
|
||||
}
|
||||
|
||||
// getSubscriptionInfoResponse returns information about the current configured subnet license for Console
|
||||
func getSubscriptionInfoResponse() (*models.License, *models.Error) {
|
||||
var licenseInfo *models.License
|
||||
client := &cluster.HTTPClient{
|
||||
Client: GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseKey := retrieveLicense()
|
||||
// validate license key and obtain license info
|
||||
licenseInfo, _, err := subscriptionValidate(client, licenseKey, "", "")
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
return licenseInfo, nil
|
||||
}
|
||||
@@ -118,8 +118,8 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
registerAdminBucketRemoteHandlers(api)
|
||||
// Register admin log search
|
||||
registerLogSearchHandlers(api)
|
||||
// Register admin subscription handlers
|
||||
registerSubscriptionHandlers(api)
|
||||
// Register admin subnet handlers
|
||||
registerSubnetHandlers(api)
|
||||
// Register Account handlers
|
||||
registerAdminTiersHandlers(api)
|
||||
|
||||
|
||||
@@ -3212,13 +3212,13 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subscription/info": {
|
||||
"/subnet/info": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Subscription info",
|
||||
"operationId": "SubscriptionInfo",
|
||||
"summary": "Subnet info",
|
||||
"operationId": "SubnetInfo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@@ -3235,6 +3235,125 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/login": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Login to subnet",
|
||||
"operationId": "SubnetLogin",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/login/mfa": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Login to subnet using mfa",
|
||||
"operationId": "SubnetLoginMFA",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginMFARequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/register": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Register cluster with Subnet",
|
||||
"operationId": "SubnetRegister",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetRegisterRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/registration-token": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Subnet registraton token",
|
||||
"operationId": "SubnetRegToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/SubnetRegTokenResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -3575,6 +3694,14 @@ func init() {
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"SubnetRegTokenResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"regToken": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"accessRule": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -5547,6 +5674,97 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetLoginMFARequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username",
|
||||
"otp",
|
||||
"mfa_token"
|
||||
],
|
||||
"properties": {
|
||||
"mfa_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetLoginRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetLoginResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"mfa_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"organizations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/subnetOrganization"
|
||||
}
|
||||
},
|
||||
"registered": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetOrganization": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accountId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"company": {
|
||||
"type": "string"
|
||||
},
|
||||
"isAccountOwner": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"shortName": {
|
||||
"type": "string"
|
||||
},
|
||||
"subscriptionStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetRegisterRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"token",
|
||||
"account_id"
|
||||
],
|
||||
"properties": {
|
||||
"account_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tier": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -9061,13 +9279,13 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subscription/info": {
|
||||
"/subnet/info": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Subscription info",
|
||||
"operationId": "SubscriptionInfo",
|
||||
"summary": "Subnet info",
|
||||
"operationId": "SubnetInfo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@@ -9084,6 +9302,125 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/login": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Login to subnet",
|
||||
"operationId": "SubnetLogin",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/login/mfa": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Login to subnet using mfa",
|
||||
"operationId": "SubnetLoginMFA",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginMFARequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetLoginResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/register": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Register cluster with Subnet",
|
||||
"operationId": "SubnetRegister",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subnetRegisterRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subnet/registration-token": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Subnet registraton token",
|
||||
"operationId": "SubnetRegToken",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/SubnetRegTokenResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -9490,6 +9827,14 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"SubnetRegTokenResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"regToken": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"WidgetDetailsOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -11516,6 +11861,97 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetLoginMFARequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username",
|
||||
"otp",
|
||||
"mfa_token"
|
||||
],
|
||||
"properties": {
|
||||
"mfa_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetLoginRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetLoginResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"mfa_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"organizations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/subnetOrganization"
|
||||
}
|
||||
},
|
||||
"registered": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetOrganization": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accountId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"company": {
|
||||
"type": "string"
|
||||
},
|
||||
"isAccountOwner": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"shortName": {
|
||||
"type": "string"
|
||||
},
|
||||
"subscriptionStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subnetRegisterRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"token",
|
||||
"account_id"
|
||||
],
|
||||
"properties": {
|
||||
"account_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tier": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -30,40 +30,40 @@ import (
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubscriptionInfoHandlerFunc turns a function with the right signature into a subscription info handler
|
||||
type SubscriptionInfoHandlerFunc func(SubscriptionInfoParams, *models.Principal) middleware.Responder
|
||||
// SubnetInfoHandlerFunc turns a function with the right signature into a subnet info handler
|
||||
type SubnetInfoHandlerFunc func(SubnetInfoParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn SubscriptionInfoHandlerFunc) Handle(params SubscriptionInfoParams, principal *models.Principal) middleware.Responder {
|
||||
func (fn SubnetInfoHandlerFunc) Handle(params SubnetInfoParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// SubscriptionInfoHandler interface for that can handle valid subscription info params
|
||||
type SubscriptionInfoHandler interface {
|
||||
Handle(SubscriptionInfoParams, *models.Principal) middleware.Responder
|
||||
// SubnetInfoHandler interface for that can handle valid subnet info params
|
||||
type SubnetInfoHandler interface {
|
||||
Handle(SubnetInfoParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewSubscriptionInfo creates a new http.Handler for the subscription info operation
|
||||
func NewSubscriptionInfo(ctx *middleware.Context, handler SubscriptionInfoHandler) *SubscriptionInfo {
|
||||
return &SubscriptionInfo{Context: ctx, Handler: handler}
|
||||
// NewSubnetInfo creates a new http.Handler for the subnet info operation
|
||||
func NewSubnetInfo(ctx *middleware.Context, handler SubnetInfoHandler) *SubnetInfo {
|
||||
return &SubnetInfo{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* SubscriptionInfo swagger:route GET /subscription/info AdminAPI subscriptionInfo
|
||||
/* SubnetInfo swagger:route GET /subnet/info AdminAPI subnetInfo
|
||||
|
||||
Subscription info
|
||||
Subnet info
|
||||
|
||||
*/
|
||||
type SubscriptionInfo struct {
|
||||
type SubnetInfo struct {
|
||||
Context *middleware.Context
|
||||
Handler SubscriptionInfoHandler
|
||||
Handler SubnetInfoHandler
|
||||
}
|
||||
|
||||
func (o *SubscriptionInfo) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
func (o *SubnetInfo) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewSubscriptionInfoParams()
|
||||
var Params = NewSubnetInfoParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
@@ -29,19 +29,19 @@ import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// NewSubscriptionInfoParams creates a new SubscriptionInfoParams object
|
||||
// NewSubnetInfoParams creates a new SubnetInfoParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewSubscriptionInfoParams() SubscriptionInfoParams {
|
||||
func NewSubnetInfoParams() SubnetInfoParams {
|
||||
|
||||
return SubscriptionInfoParams{}
|
||||
return SubnetInfoParams{}
|
||||
}
|
||||
|
||||
// SubscriptionInfoParams contains all the bound params for the subscription info operation
|
||||
// SubnetInfoParams contains all the bound params for the subnet info operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters SubscriptionInfo
|
||||
type SubscriptionInfoParams struct {
|
||||
// swagger:parameters SubnetInfo
|
||||
type SubnetInfoParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
@@ -50,8 +50,8 @@ type SubscriptionInfoParams struct {
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewSubscriptionInfoParams() beforehand.
|
||||
func (o *SubscriptionInfoParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
// To ensure default values, the struct must have been initialized with NewSubnetInfoParams() beforehand.
|
||||
func (o *SubnetInfoParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
@@ -30,14 +30,14 @@ import (
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubscriptionInfoOKCode is the HTTP code returned for type SubscriptionInfoOK
|
||||
const SubscriptionInfoOKCode int = 200
|
||||
// SubnetInfoOKCode is the HTTP code returned for type SubnetInfoOK
|
||||
const SubnetInfoOKCode int = 200
|
||||
|
||||
/*SubscriptionInfoOK A successful response.
|
||||
/*SubnetInfoOK A successful response.
|
||||
|
||||
swagger:response subscriptionInfoOK
|
||||
swagger:response subnetInfoOK
|
||||
*/
|
||||
type SubscriptionInfoOK struct {
|
||||
type SubnetInfoOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
@@ -45,25 +45,25 @@ type SubscriptionInfoOK struct {
|
||||
Payload *models.License `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubscriptionInfoOK creates SubscriptionInfoOK with default headers values
|
||||
func NewSubscriptionInfoOK() *SubscriptionInfoOK {
|
||||
// NewSubnetInfoOK creates SubnetInfoOK with default headers values
|
||||
func NewSubnetInfoOK() *SubnetInfoOK {
|
||||
|
||||
return &SubscriptionInfoOK{}
|
||||
return &SubnetInfoOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subscription info o k response
|
||||
func (o *SubscriptionInfoOK) WithPayload(payload *models.License) *SubscriptionInfoOK {
|
||||
// WithPayload adds the payload to the subnet info o k response
|
||||
func (o *SubnetInfoOK) WithPayload(payload *models.License) *SubnetInfoOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subscription info o k response
|
||||
func (o *SubscriptionInfoOK) SetPayload(payload *models.License) {
|
||||
// SetPayload sets the payload to the subnet info o k response
|
||||
func (o *SubnetInfoOK) SetPayload(payload *models.License) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubscriptionInfoOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
func (o *SubnetInfoOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
@@ -74,11 +74,11 @@ func (o *SubscriptionInfoOK) WriteResponse(rw http.ResponseWriter, producer runt
|
||||
}
|
||||
}
|
||||
|
||||
/*SubscriptionInfoDefault Generic error response.
|
||||
/*SubnetInfoDefault Generic error response.
|
||||
|
||||
swagger:response subscriptionInfoDefault
|
||||
swagger:response subnetInfoDefault
|
||||
*/
|
||||
type SubscriptionInfoDefault struct {
|
||||
type SubnetInfoDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
@@ -87,41 +87,41 @@ type SubscriptionInfoDefault struct {
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubscriptionInfoDefault creates SubscriptionInfoDefault with default headers values
|
||||
func NewSubscriptionInfoDefault(code int) *SubscriptionInfoDefault {
|
||||
// NewSubnetInfoDefault creates SubnetInfoDefault with default headers values
|
||||
func NewSubnetInfoDefault(code int) *SubnetInfoDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &SubscriptionInfoDefault{
|
||||
return &SubnetInfoDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the subscription info default response
|
||||
func (o *SubscriptionInfoDefault) WithStatusCode(code int) *SubscriptionInfoDefault {
|
||||
// WithStatusCode adds the status to the subnet info default response
|
||||
func (o *SubnetInfoDefault) WithStatusCode(code int) *SubnetInfoDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the subscription info default response
|
||||
func (o *SubscriptionInfoDefault) SetStatusCode(code int) {
|
||||
// SetStatusCode sets the status to the subnet info default response
|
||||
func (o *SubnetInfoDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subscription info default response
|
||||
func (o *SubscriptionInfoDefault) WithPayload(payload *models.Error) *SubscriptionInfoDefault {
|
||||
// WithPayload adds the payload to the subnet info default response
|
||||
func (o *SubnetInfoDefault) WithPayload(payload *models.Error) *SubnetInfoDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subscription info default response
|
||||
func (o *SubscriptionInfoDefault) SetPayload(payload *models.Error) {
|
||||
// SetPayload sets the payload to the subnet info default response
|
||||
func (o *SubnetInfoDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubscriptionInfoDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
func (o *SubnetInfoDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
@@ -28,15 +28,15 @@ import (
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// SubscriptionInfoURL generates an URL for the subscription info operation
|
||||
type SubscriptionInfoURL struct {
|
||||
// SubnetInfoURL generates an URL for the subnet info operation
|
||||
type SubnetInfoURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubscriptionInfoURL) WithBasePath(bp string) *SubscriptionInfoURL {
|
||||
func (o *SubnetInfoURL) WithBasePath(bp string) *SubnetInfoURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
@@ -44,15 +44,15 @@ func (o *SubscriptionInfoURL) WithBasePath(bp string) *SubscriptionInfoURL {
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubscriptionInfoURL) SetBasePath(bp string) {
|
||||
func (o *SubnetInfoURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *SubscriptionInfoURL) Build() (*url.URL, error) {
|
||||
func (o *SubnetInfoURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/subscription/info"
|
||||
var _path = "/subnet/info"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
@@ -64,7 +64,7 @@ func (o *SubscriptionInfoURL) Build() (*url.URL, error) {
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *SubscriptionInfoURL) Must(u *url.URL, err error) *url.URL {
|
||||
func (o *SubnetInfoURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -75,17 +75,17 @@ func (o *SubscriptionInfoURL) Must(u *url.URL, err error) *url.URL {
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *SubscriptionInfoURL) String() string {
|
||||
func (o *SubnetInfoURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *SubscriptionInfoURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
func (o *SubnetInfoURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on SubscriptionInfoURL")
|
||||
return nil, errors.New("scheme is required for a full url on SubnetInfoURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on SubscriptionInfoURL")
|
||||
return nil, errors.New("host is required for a full url on SubnetInfoURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
@@ -99,6 +99,6 @@ func (o *SubscriptionInfoURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *SubscriptionInfoURL) StringFull(scheme, host string) string {
|
||||
func (o *SubnetInfoURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
88
restapi/operations/admin_api/subnet_login.go
Normal file
88
restapi/operations/admin_api/subnet_login.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetLoginHandlerFunc turns a function with the right signature into a subnet login handler
|
||||
type SubnetLoginHandlerFunc func(SubnetLoginParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn SubnetLoginHandlerFunc) Handle(params SubnetLoginParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// SubnetLoginHandler interface for that can handle valid subnet login params
|
||||
type SubnetLoginHandler interface {
|
||||
Handle(SubnetLoginParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewSubnetLogin creates a new http.Handler for the subnet login operation
|
||||
func NewSubnetLogin(ctx *middleware.Context, handler SubnetLoginHandler) *SubnetLogin {
|
||||
return &SubnetLogin{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* SubnetLogin swagger:route POST /subnet/login AdminAPI subnetLogin
|
||||
|
||||
Login to subnet
|
||||
|
||||
*/
|
||||
type SubnetLogin struct {
|
||||
Context *middleware.Context
|
||||
Handler SubnetLoginHandler
|
||||
}
|
||||
|
||||
func (o *SubnetLogin) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewSubnetLoginParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
88
restapi/operations/admin_api/subnet_login_m_f_a.go
Normal file
88
restapi/operations/admin_api/subnet_login_m_f_a.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetLoginMFAHandlerFunc turns a function with the right signature into a subnet login m f a handler
|
||||
type SubnetLoginMFAHandlerFunc func(SubnetLoginMFAParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn SubnetLoginMFAHandlerFunc) Handle(params SubnetLoginMFAParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// SubnetLoginMFAHandler interface for that can handle valid subnet login m f a params
|
||||
type SubnetLoginMFAHandler interface {
|
||||
Handle(SubnetLoginMFAParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewSubnetLoginMFA creates a new http.Handler for the subnet login m f a operation
|
||||
func NewSubnetLoginMFA(ctx *middleware.Context, handler SubnetLoginMFAHandler) *SubnetLoginMFA {
|
||||
return &SubnetLoginMFA{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* SubnetLoginMFA swagger:route POST /subnet/login/mfa AdminAPI subnetLoginMFA
|
||||
|
||||
Login to subnet using mfa
|
||||
|
||||
*/
|
||||
type SubnetLoginMFA struct {
|
||||
Context *middleware.Context
|
||||
Handler SubnetLoginMFAHandler
|
||||
}
|
||||
|
||||
func (o *SubnetLoginMFA) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewSubnetLoginMFAParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
102
restapi/operations/admin_api/subnet_login_m_f_a_parameters.go
Normal file
102
restapi/operations/admin_api/subnet_login_m_f_a_parameters.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewSubnetLoginMFAParams creates a new SubnetLoginMFAParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewSubnetLoginMFAParams() SubnetLoginMFAParams {
|
||||
|
||||
return SubnetLoginMFAParams{}
|
||||
}
|
||||
|
||||
// SubnetLoginMFAParams contains all the bound params for the subnet login m f a operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters SubnetLoginMFA
|
||||
type SubnetLoginMFAParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.SubnetLoginMFARequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewSubnetLoginMFAParams() beforehand.
|
||||
func (o *SubnetLoginMFAParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.SubnetLoginMFARequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(context.Background())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
133
restapi/operations/admin_api/subnet_login_m_f_a_responses.go
Normal file
133
restapi/operations/admin_api/subnet_login_m_f_a_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetLoginMFAOKCode is the HTTP code returned for type SubnetLoginMFAOK
|
||||
const SubnetLoginMFAOKCode int = 200
|
||||
|
||||
/*SubnetLoginMFAOK A successful response.
|
||||
|
||||
swagger:response subnetLoginMFAOK
|
||||
*/
|
||||
type SubnetLoginMFAOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.SubnetLoginResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubnetLoginMFAOK creates SubnetLoginMFAOK with default headers values
|
||||
func NewSubnetLoginMFAOK() *SubnetLoginMFAOK {
|
||||
|
||||
return &SubnetLoginMFAOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subnet login m f a o k response
|
||||
func (o *SubnetLoginMFAOK) WithPayload(payload *models.SubnetLoginResponse) *SubnetLoginMFAOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subnet login m f a o k response
|
||||
func (o *SubnetLoginMFAOK) SetPayload(payload *models.SubnetLoginResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetLoginMFAOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*SubnetLoginMFADefault Generic error response.
|
||||
|
||||
swagger:response subnetLoginMFADefault
|
||||
*/
|
||||
type SubnetLoginMFADefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubnetLoginMFADefault creates SubnetLoginMFADefault with default headers values
|
||||
func NewSubnetLoginMFADefault(code int) *SubnetLoginMFADefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &SubnetLoginMFADefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the subnet login m f a default response
|
||||
func (o *SubnetLoginMFADefault) WithStatusCode(code int) *SubnetLoginMFADefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the subnet login m f a default response
|
||||
func (o *SubnetLoginMFADefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subnet login m f a default response
|
||||
func (o *SubnetLoginMFADefault) WithPayload(payload *models.Error) *SubnetLoginMFADefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subnet login m f a default response
|
||||
func (o *SubnetLoginMFADefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetLoginMFADefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
restapi/operations/admin_api/subnet_login_m_f_a_urlbuilder.go
Normal file
104
restapi/operations/admin_api/subnet_login_m_f_a_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// SubnetLoginMFAURL generates an URL for the subnet login m f a operation
|
||||
type SubnetLoginMFAURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetLoginMFAURL) WithBasePath(bp string) *SubnetLoginMFAURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetLoginMFAURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *SubnetLoginMFAURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/subnet/login/mfa"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *SubnetLoginMFAURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *SubnetLoginMFAURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *SubnetLoginMFAURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on SubnetLoginMFAURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on SubnetLoginMFAURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *SubnetLoginMFAURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
102
restapi/operations/admin_api/subnet_login_parameters.go
Normal file
102
restapi/operations/admin_api/subnet_login_parameters.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewSubnetLoginParams creates a new SubnetLoginParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewSubnetLoginParams() SubnetLoginParams {
|
||||
|
||||
return SubnetLoginParams{}
|
||||
}
|
||||
|
||||
// SubnetLoginParams contains all the bound params for the subnet login operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters SubnetLogin
|
||||
type SubnetLoginParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.SubnetLoginRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewSubnetLoginParams() beforehand.
|
||||
func (o *SubnetLoginParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.SubnetLoginRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(context.Background())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
133
restapi/operations/admin_api/subnet_login_responses.go
Normal file
133
restapi/operations/admin_api/subnet_login_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetLoginOKCode is the HTTP code returned for type SubnetLoginOK
|
||||
const SubnetLoginOKCode int = 200
|
||||
|
||||
/*SubnetLoginOK A successful response.
|
||||
|
||||
swagger:response subnetLoginOK
|
||||
*/
|
||||
type SubnetLoginOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.SubnetLoginResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubnetLoginOK creates SubnetLoginOK with default headers values
|
||||
func NewSubnetLoginOK() *SubnetLoginOK {
|
||||
|
||||
return &SubnetLoginOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subnet login o k response
|
||||
func (o *SubnetLoginOK) WithPayload(payload *models.SubnetLoginResponse) *SubnetLoginOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subnet login o k response
|
||||
func (o *SubnetLoginOK) SetPayload(payload *models.SubnetLoginResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetLoginOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*SubnetLoginDefault Generic error response.
|
||||
|
||||
swagger:response subnetLoginDefault
|
||||
*/
|
||||
type SubnetLoginDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubnetLoginDefault creates SubnetLoginDefault with default headers values
|
||||
func NewSubnetLoginDefault(code int) *SubnetLoginDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &SubnetLoginDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the subnet login default response
|
||||
func (o *SubnetLoginDefault) WithStatusCode(code int) *SubnetLoginDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the subnet login default response
|
||||
func (o *SubnetLoginDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subnet login default response
|
||||
func (o *SubnetLoginDefault) WithPayload(payload *models.Error) *SubnetLoginDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subnet login default response
|
||||
func (o *SubnetLoginDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetLoginDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
restapi/operations/admin_api/subnet_login_urlbuilder.go
Normal file
104
restapi/operations/admin_api/subnet_login_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// SubnetLoginURL generates an URL for the subnet login operation
|
||||
type SubnetLoginURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetLoginURL) WithBasePath(bp string) *SubnetLoginURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetLoginURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *SubnetLoginURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/subnet/login"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *SubnetLoginURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *SubnetLoginURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *SubnetLoginURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on SubnetLoginURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on SubnetLoginURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *SubnetLoginURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
88
restapi/operations/admin_api/subnet_reg_token.go
Normal file
88
restapi/operations/admin_api/subnet_reg_token.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetRegTokenHandlerFunc turns a function with the right signature into a subnet reg token handler
|
||||
type SubnetRegTokenHandlerFunc func(SubnetRegTokenParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn SubnetRegTokenHandlerFunc) Handle(params SubnetRegTokenParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// SubnetRegTokenHandler interface for that can handle valid subnet reg token params
|
||||
type SubnetRegTokenHandler interface {
|
||||
Handle(SubnetRegTokenParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewSubnetRegToken creates a new http.Handler for the subnet reg token operation
|
||||
func NewSubnetRegToken(ctx *middleware.Context, handler SubnetRegTokenHandler) *SubnetRegToken {
|
||||
return &SubnetRegToken{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* SubnetRegToken swagger:route GET /subnet/registration-token AdminAPI subnetRegToken
|
||||
|
||||
Subnet registraton token
|
||||
|
||||
*/
|
||||
type SubnetRegToken struct {
|
||||
Context *middleware.Context
|
||||
Handler SubnetRegTokenHandler
|
||||
}
|
||||
|
||||
func (o *SubnetRegToken) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewSubnetRegTokenParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
63
restapi/operations/admin_api/subnet_reg_token_parameters.go
Normal file
63
restapi/operations/admin_api/subnet_reg_token_parameters.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// NewSubnetRegTokenParams creates a new SubnetRegTokenParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewSubnetRegTokenParams() SubnetRegTokenParams {
|
||||
|
||||
return SubnetRegTokenParams{}
|
||||
}
|
||||
|
||||
// SubnetRegTokenParams contains all the bound params for the subnet reg token operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters SubnetRegToken
|
||||
type SubnetRegTokenParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewSubnetRegTokenParams() beforehand.
|
||||
func (o *SubnetRegTokenParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
133
restapi/operations/admin_api/subnet_reg_token_responses.go
Normal file
133
restapi/operations/admin_api/subnet_reg_token_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetRegTokenOKCode is the HTTP code returned for type SubnetRegTokenOK
|
||||
const SubnetRegTokenOKCode int = 200
|
||||
|
||||
/*SubnetRegTokenOK A successful response.
|
||||
|
||||
swagger:response subnetRegTokenOK
|
||||
*/
|
||||
type SubnetRegTokenOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.SubnetRegTokenResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubnetRegTokenOK creates SubnetRegTokenOK with default headers values
|
||||
func NewSubnetRegTokenOK() *SubnetRegTokenOK {
|
||||
|
||||
return &SubnetRegTokenOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subnet reg token o k response
|
||||
func (o *SubnetRegTokenOK) WithPayload(payload *models.SubnetRegTokenResponse) *SubnetRegTokenOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subnet reg token o k response
|
||||
func (o *SubnetRegTokenOK) SetPayload(payload *models.SubnetRegTokenResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetRegTokenOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*SubnetRegTokenDefault Generic error response.
|
||||
|
||||
swagger:response subnetRegTokenDefault
|
||||
*/
|
||||
type SubnetRegTokenDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubnetRegTokenDefault creates SubnetRegTokenDefault with default headers values
|
||||
func NewSubnetRegTokenDefault(code int) *SubnetRegTokenDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &SubnetRegTokenDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the subnet reg token default response
|
||||
func (o *SubnetRegTokenDefault) WithStatusCode(code int) *SubnetRegTokenDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the subnet reg token default response
|
||||
func (o *SubnetRegTokenDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subnet reg token default response
|
||||
func (o *SubnetRegTokenDefault) WithPayload(payload *models.Error) *SubnetRegTokenDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subnet reg token default response
|
||||
func (o *SubnetRegTokenDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetRegTokenDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
restapi/operations/admin_api/subnet_reg_token_urlbuilder.go
Normal file
104
restapi/operations/admin_api/subnet_reg_token_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// SubnetRegTokenURL generates an URL for the subnet reg token operation
|
||||
type SubnetRegTokenURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetRegTokenURL) WithBasePath(bp string) *SubnetRegTokenURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetRegTokenURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *SubnetRegTokenURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/subnet/registration-token"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *SubnetRegTokenURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *SubnetRegTokenURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *SubnetRegTokenURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on SubnetRegTokenURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on SubnetRegTokenURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *SubnetRegTokenURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
88
restapi/operations/admin_api/subnet_register.go
Normal file
88
restapi/operations/admin_api/subnet_register.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetRegisterHandlerFunc turns a function with the right signature into a subnet register handler
|
||||
type SubnetRegisterHandlerFunc func(SubnetRegisterParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn SubnetRegisterHandlerFunc) Handle(params SubnetRegisterParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// SubnetRegisterHandler interface for that can handle valid subnet register params
|
||||
type SubnetRegisterHandler interface {
|
||||
Handle(SubnetRegisterParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewSubnetRegister creates a new http.Handler for the subnet register operation
|
||||
func NewSubnetRegister(ctx *middleware.Context, handler SubnetRegisterHandler) *SubnetRegister {
|
||||
return &SubnetRegister{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* SubnetRegister swagger:route POST /subnet/register AdminAPI subnetRegister
|
||||
|
||||
Register cluster with Subnet
|
||||
|
||||
*/
|
||||
type SubnetRegister struct {
|
||||
Context *middleware.Context
|
||||
Handler SubnetRegisterHandler
|
||||
}
|
||||
|
||||
func (o *SubnetRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewSubnetRegisterParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
102
restapi/operations/admin_api/subnet_register_parameters.go
Normal file
102
restapi/operations/admin_api/subnet_register_parameters.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewSubnetRegisterParams creates a new SubnetRegisterParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewSubnetRegisterParams() SubnetRegisterParams {
|
||||
|
||||
return SubnetRegisterParams{}
|
||||
}
|
||||
|
||||
// SubnetRegisterParams contains all the bound params for the subnet register operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters SubnetRegister
|
||||
type SubnetRegisterParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.SubnetRegisterRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewSubnetRegisterParams() beforehand.
|
||||
func (o *SubnetRegisterParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.SubnetRegisterRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(context.Background())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
113
restapi/operations/admin_api/subnet_register_responses.go
Normal file
113
restapi/operations/admin_api/subnet_register_responses.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SubnetRegisterOKCode is the HTTP code returned for type SubnetRegisterOK
|
||||
const SubnetRegisterOKCode int = 200
|
||||
|
||||
/*SubnetRegisterOK A successful response.
|
||||
|
||||
swagger:response subnetRegisterOK
|
||||
*/
|
||||
type SubnetRegisterOK struct {
|
||||
}
|
||||
|
||||
// NewSubnetRegisterOK creates SubnetRegisterOK with default headers values
|
||||
func NewSubnetRegisterOK() *SubnetRegisterOK {
|
||||
|
||||
return &SubnetRegisterOK{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetRegisterOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(200)
|
||||
}
|
||||
|
||||
/*SubnetRegisterDefault Generic error response.
|
||||
|
||||
swagger:response subnetRegisterDefault
|
||||
*/
|
||||
type SubnetRegisterDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSubnetRegisterDefault creates SubnetRegisterDefault with default headers values
|
||||
func NewSubnetRegisterDefault(code int) *SubnetRegisterDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &SubnetRegisterDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the subnet register default response
|
||||
func (o *SubnetRegisterDefault) WithStatusCode(code int) *SubnetRegisterDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the subnet register default response
|
||||
func (o *SubnetRegisterDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the subnet register default response
|
||||
func (o *SubnetRegisterDefault) WithPayload(payload *models.Error) *SubnetRegisterDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the subnet register default response
|
||||
func (o *SubnetRegisterDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SubnetRegisterDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
restapi/operations/admin_api/subnet_register_urlbuilder.go
Normal file
104
restapi/operations/admin_api/subnet_register_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 admin_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// SubnetRegisterURL generates an URL for the subnet register operation
|
||||
type SubnetRegisterURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetRegisterURL) WithBasePath(bp string) *SubnetRegisterURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SubnetRegisterURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *SubnetRegisterURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/subnet/register"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *SubnetRegisterURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *SubnetRegisterURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *SubnetRegisterURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on SubnetRegisterURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on SubnetRegisterURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *SubnetRegisterURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -350,8 +350,20 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
UserAPIShareObjectHandler: user_api.ShareObjectHandlerFunc(func(params user_api.ShareObjectParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.ShareObject has not yet been implemented")
|
||||
}),
|
||||
AdminAPISubscriptionInfoHandler: admin_api.SubscriptionInfoHandlerFunc(func(params admin_api.SubscriptionInfoParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubscriptionInfo has not yet been implemented")
|
||||
AdminAPISubnetInfoHandler: admin_api.SubnetInfoHandlerFunc(func(params admin_api.SubnetInfoParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubnetInfo has not yet been implemented")
|
||||
}),
|
||||
AdminAPISubnetLoginHandler: admin_api.SubnetLoginHandlerFunc(func(params admin_api.SubnetLoginParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubnetLogin has not yet been implemented")
|
||||
}),
|
||||
AdminAPISubnetLoginMFAHandler: admin_api.SubnetLoginMFAHandlerFunc(func(params admin_api.SubnetLoginMFAParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubnetLoginMFA has not yet been implemented")
|
||||
}),
|
||||
AdminAPISubnetRegTokenHandler: admin_api.SubnetRegTokenHandlerFunc(func(params admin_api.SubnetRegTokenParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubnetRegToken has not yet been implemented")
|
||||
}),
|
||||
AdminAPISubnetRegisterHandler: admin_api.SubnetRegisterHandlerFunc(func(params admin_api.SubnetRegisterParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SubnetRegister has not yet been implemented")
|
||||
}),
|
||||
AdminAPITiersListHandler: admin_api.TiersListHandlerFunc(func(params admin_api.TiersListParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.TiersList has not yet been implemented")
|
||||
@@ -616,8 +628,16 @@ type ConsoleAPI struct {
|
||||
AdminAPISetPolicyMultipleHandler admin_api.SetPolicyMultipleHandler
|
||||
// UserAPIShareObjectHandler sets the operation handler for the share object operation
|
||||
UserAPIShareObjectHandler user_api.ShareObjectHandler
|
||||
// AdminAPISubscriptionInfoHandler sets the operation handler for the subscription info operation
|
||||
AdminAPISubscriptionInfoHandler admin_api.SubscriptionInfoHandler
|
||||
// AdminAPISubnetInfoHandler sets the operation handler for the subnet info operation
|
||||
AdminAPISubnetInfoHandler admin_api.SubnetInfoHandler
|
||||
// AdminAPISubnetLoginHandler sets the operation handler for the subnet login operation
|
||||
AdminAPISubnetLoginHandler admin_api.SubnetLoginHandler
|
||||
// AdminAPISubnetLoginMFAHandler sets the operation handler for the subnet login m f a operation
|
||||
AdminAPISubnetLoginMFAHandler admin_api.SubnetLoginMFAHandler
|
||||
// AdminAPISubnetRegTokenHandler sets the operation handler for the subnet reg token operation
|
||||
AdminAPISubnetRegTokenHandler admin_api.SubnetRegTokenHandler
|
||||
// AdminAPISubnetRegisterHandler sets the operation handler for the subnet register operation
|
||||
AdminAPISubnetRegisterHandler admin_api.SubnetRegisterHandler
|
||||
// AdminAPITiersListHandler sets the operation handler for the tiers list operation
|
||||
AdminAPITiersListHandler admin_api.TiersListHandler
|
||||
// UserAPIUpdateBucketLifecycleHandler sets the operation handler for the update bucket lifecycle operation
|
||||
@@ -1002,8 +1022,20 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.UserAPIShareObjectHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.ShareObjectHandler")
|
||||
}
|
||||
if o.AdminAPISubscriptionInfoHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubscriptionInfoHandler")
|
||||
if o.AdminAPISubnetInfoHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubnetInfoHandler")
|
||||
}
|
||||
if o.AdminAPISubnetLoginHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubnetLoginHandler")
|
||||
}
|
||||
if o.AdminAPISubnetLoginMFAHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubnetLoginMFAHandler")
|
||||
}
|
||||
if o.AdminAPISubnetRegTokenHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubnetRegTokenHandler")
|
||||
}
|
||||
if o.AdminAPISubnetRegisterHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SubnetRegisterHandler")
|
||||
}
|
||||
if o.AdminAPITiersListHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.TiersListHandler")
|
||||
@@ -1508,7 +1540,23 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/subscription/info"] = admin_api.NewSubscriptionInfo(o.context, o.AdminAPISubscriptionInfoHandler)
|
||||
o.handlers["GET"]["/subnet/info"] = admin_api.NewSubnetInfo(o.context, o.AdminAPISubnetInfoHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/subnet/login"] = admin_api.NewSubnetLogin(o.context, o.AdminAPISubnetLoginHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/subnet/login/mfa"] = admin_api.NewSubnetLoginMFA(o.context, o.AdminAPISubnetLoginMFAHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/subnet/registration-token"] = admin_api.NewSubnetRegToken(o.context, o.AdminAPISubnetRegTokenHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/subnet/register"] = admin_api.NewSubnetRegister(o.context, o.AdminAPISubnetRegisterHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ securityDefinitions:
|
||||
tokenUrl: http://min.io
|
||||
# Apply the key security definition to all APIs
|
||||
security:
|
||||
- key: []
|
||||
- key: [ ]
|
||||
paths:
|
||||
/login:
|
||||
get:
|
||||
@@ -35,7 +35,7 @@ paths:
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
# Exclude this API from the authentication requirement
|
||||
security: []
|
||||
security: [ ]
|
||||
tags:
|
||||
- UserAPI
|
||||
post:
|
||||
@@ -55,7 +55,7 @@ paths:
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
# Exclude this API from the authentication requirement
|
||||
security: []
|
||||
security: [ ]
|
||||
tags:
|
||||
- UserAPI
|
||||
/login/oauth2/auth:
|
||||
@@ -75,7 +75,7 @@ paths:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
security: []
|
||||
security: [ ]
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
@@ -2067,11 +2067,25 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
/subscription/info:
|
||||
/subnet/registration-token:
|
||||
get:
|
||||
summary: Subscription info
|
||||
operationId: SubscriptionInfo
|
||||
summary: Subnet registraton token
|
||||
operationId: SubnetRegToken
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/SubnetRegTokenResponse"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
/subnet/info:
|
||||
get:
|
||||
summary: Subnet info
|
||||
operationId: SubnetInfo
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
@@ -2083,6 +2097,71 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
/subnet/register:
|
||||
post:
|
||||
summary: Register cluster with Subnet
|
||||
operationId: SubnetRegister
|
||||
parameters:
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/subnetRegisterRequest"
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
# schema:
|
||||
# $ref: "#/definitions/subnetRegisterResponse"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
/subnet/login:
|
||||
post:
|
||||
summary: Login to subnet
|
||||
operationId: SubnetLogin
|
||||
parameters:
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/subnetLoginRequest"
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/subnetLoginResponse"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
/subnet/login/mfa:
|
||||
post:
|
||||
summary: Login to subnet using mfa
|
||||
operationId: SubnetLoginMFA
|
||||
parameters:
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/subnetLoginMFARequest"
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/subnetLoginResponse"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
/admin/info:
|
||||
get:
|
||||
@@ -2386,7 +2465,7 @@ paths:
|
||||
- name: order
|
||||
in: query
|
||||
type: string
|
||||
enum: [timeDesc, timeAsc]
|
||||
enum: [ timeDesc, timeAsc ]
|
||||
default: timeDesc
|
||||
- name: timeStart
|
||||
in: query
|
||||
@@ -3152,7 +3231,7 @@ definitions:
|
||||
properties:
|
||||
loginStrategy:
|
||||
type: string
|
||||
enum: [form, redirect, service-account]
|
||||
enum: [ form, redirect, service-account ]
|
||||
redirect:
|
||||
type: string
|
||||
loginOauth2AuthRequest:
|
||||
@@ -3238,7 +3317,7 @@ definitions:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: [ok]
|
||||
enum: [ ok ]
|
||||
operator:
|
||||
type: boolean
|
||||
distributedMode:
|
||||
@@ -3259,7 +3338,7 @@ definitions:
|
||||
type: string
|
||||
values:
|
||||
type: array
|
||||
items: {}
|
||||
items: { }
|
||||
resultTarget:
|
||||
type: object
|
||||
properties:
|
||||
@@ -3539,7 +3618,7 @@ definitions:
|
||||
type: string
|
||||
service:
|
||||
type: string
|
||||
enum: [replication]
|
||||
enum: [ replication ]
|
||||
syncMode:
|
||||
type: string
|
||||
bandwidth:
|
||||
@@ -4015,4 +4094,75 @@ definitions:
|
||||
properties:
|
||||
objectMetadata:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
additionalProperties: true
|
||||
|
||||
subnetLoginResponse:
|
||||
type: object
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
organizations:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/subnetOrganization"
|
||||
mfa_token:
|
||||
type: string
|
||||
registered:
|
||||
type: boolean
|
||||
|
||||
subnetLoginRequest:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
apiKey:
|
||||
type: string
|
||||
|
||||
subnetLoginMFARequest:
|
||||
type: object
|
||||
required:
|
||||
- username
|
||||
- otp
|
||||
- mfa_token
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
otp:
|
||||
type: string
|
||||
mfa_token:
|
||||
type: string
|
||||
|
||||
subnetRegisterRequest:
|
||||
type: object
|
||||
required:
|
||||
- token
|
||||
- account_id
|
||||
properties:
|
||||
token:
|
||||
type: string
|
||||
account_id:
|
||||
type: string
|
||||
|
||||
SubnetRegTokenResponse:
|
||||
type: object
|
||||
properties:
|
||||
regToken:
|
||||
type: string
|
||||
|
||||
subnetOrganization:
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
type: integer
|
||||
accountId:
|
||||
type: integer
|
||||
subscriptionStatus:
|
||||
type: string
|
||||
isAccountOwner:
|
||||
type: boolean
|
||||
company:
|
||||
type: string
|
||||
shortName:
|
||||
type: string
|
||||
Reference in New Issue
Block a user