Adding key creation in tenant wizard. (#697)

Signed-off-by: Adam Stafford <adam@minio.io>

Co-authored-by: Adam Stafford <adamstafford@MacBook-Pro-van-Adam-2.local>
This commit is contained in:
adfost
2021-04-22 14:18:14 -07:00
committed by GitHub
parent d60cac0122
commit ca742b781f
19 changed files with 435 additions and 97 deletions

View File

@@ -23,6 +23,8 @@ package models
// Editing this file might prove futile when you re-run the swagger generate command
import (
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
@@ -37,6 +39,9 @@ type IdpConfiguration struct {
// active directory
ActiveDirectory *IdpConfigurationActiveDirectory `json:"active_directory,omitempty"`
// keys
Keys []*IdpConfigurationKeysItems0 `json:"keys"`
// oidc
Oidc *IdpConfigurationOidc `json:"oidc,omitempty"`
}
@@ -49,6 +54,10 @@ func (m *IdpConfiguration) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateKeys(formats); err != nil {
res = append(res, err)
}
if err := m.validateOidc(formats); err != nil {
res = append(res, err)
}
@@ -77,6 +86,31 @@ func (m *IdpConfiguration) validateActiveDirectory(formats strfmt.Registry) erro
return nil
}
func (m *IdpConfiguration) validateKeys(formats strfmt.Registry) error {
if swag.IsZero(m.Keys) { // not required
return nil
}
for i := 0; i < len(m.Keys); i++ {
if swag.IsZero(m.Keys[i]) { // not required
continue
}
if m.Keys[i] != nil {
if err := m.Keys[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("keys" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *IdpConfiguration) validateOidc(formats strfmt.Registry) error {
if swag.IsZero(m.Oidc) { // not required
@@ -213,6 +247,74 @@ func (m *IdpConfigurationActiveDirectory) UnmarshalBinary(b []byte) error {
return nil
}
// IdpConfigurationKeysItems0 idp configuration keys items0
//
// swagger:model IdpConfigurationKeysItems0
type IdpConfigurationKeysItems0 struct {
// access key
// Required: true
AccessKey *string `json:"access_key"`
// secret key
// Required: true
SecretKey *string `json:"secret_key"`
}
// Validate validates this idp configuration keys items0
func (m *IdpConfigurationKeysItems0) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAccessKey(formats); err != nil {
res = append(res, err)
}
if err := m.validateSecretKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IdpConfigurationKeysItems0) validateAccessKey(formats strfmt.Registry) error {
if err := validate.Required("access_key", "body", m.AccessKey); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationKeysItems0) validateSecretKey(formats strfmt.Registry) error {
if err := validate.Required("secret_key", "body", m.SecretKey); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *IdpConfigurationKeysItems0) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IdpConfigurationKeysItems0) UnmarshalBinary(b []byte) error {
var res IdpConfigurationKeysItems0
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IdpConfigurationOidc idp configuration oidc
//
// swagger:model IdpConfigurationOidc

10
node_modules/.yarn-integrity generated vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"systemParams": "darwin-x64-88",
"modulesFolders": [],
"flags": [],
"linkedModules": [],
"topLevelPatterns": [],
"lockfileEntries": {},
"files": [],
"artifacts": {}
}

View File

@@ -1,25 +1,25 @@
{
"files": {
"main.css": "/static/css/main.a19f3d53.chunk.css",
"main.js": "/static/js/main.fb87f627.chunk.js",
"main.js.map": "/static/js/main.fb87f627.chunk.js.map",
"main.js": "/static/js/main.0888f199.chunk.js",
"main.js.map": "/static/js/main.0888f199.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.f48e99e5.js",
"runtime-main.js.map": "/static/js/runtime-main.f48e99e5.js.map",
"static/css/2.f324abd6.chunk.css": "/static/css/2.f324abd6.chunk.css",
"static/js/2.26e254ca.chunk.js": "/static/js/2.26e254ca.chunk.js",
"static/js/2.26e254ca.chunk.js.map": "/static/js/2.26e254ca.chunk.js.map",
"static/js/2.a71657cf.chunk.js": "/static/js/2.a71657cf.chunk.js",
"static/js/2.a71657cf.chunk.js.map": "/static/js/2.a71657cf.chunk.js.map",
"index.html": "/index.html",
"static/css/2.f324abd6.chunk.css.map": "/static/css/2.f324abd6.chunk.css.map",
"static/css/main.a19f3d53.chunk.css.map": "/static/css/main.a19f3d53.chunk.css.map",
"static/js/2.26e254ca.chunk.js.LICENSE.txt": "/static/js/2.26e254ca.chunk.js.LICENSE.txt",
"static/js/2.a71657cf.chunk.js.LICENSE.txt": "/static/js/2.a71657cf.chunk.js.LICENSE.txt",
"static/media/minio_console_logo.0837460e.svg": "/static/media/minio_console_logo.0837460e.svg",
"static/media/minio_operator_logo.1312b7c9.svg": "/static/media/minio_operator_logo.1312b7c9.svg"
},
"entrypoints": [
"static/js/runtime-main.f48e99e5.js",
"static/css/2.f324abd6.chunk.css",
"static/js/2.26e254ca.chunk.js",
"static/js/2.a71657cf.chunk.js",
"static/css/main.a19f3d53.chunk.css",
"static/js/main.fb87f627.chunk.js"
"static/js/main.0888f199.chunk.js"
]
}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.f324abd6.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.26e254ca.chunk.js"></script><script src="/static/js/main.fb87f627.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.f324abd6.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.a71657cf.chunk.js"></script><script src="/static/js/main.0888f199.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -54,6 +54,8 @@ export interface ITenantCreator {
enable_tls: boolean;
access_key: string;
secret_key: string;
access_keys: string[];
secret_keys: string[];
image: string;
console_image: string;
expose_minio: boolean;
@@ -66,8 +68,8 @@ export interface ITenantCreator {
idp?: IIDPConfiguration;
annotations?: Object;
image_registry?: ImageRegistry;
logSearchConfiguration?: LogSearchConfiguration,
prometheusConfiguration?: PrometheusConfiguration,
logSearchConfiguration?: LogSearchConfiguration;
prometheusConfiguration?: PrometheusConfiguration;
}
export interface ImageRegistry {

View File

@@ -59,7 +59,6 @@ import EnableVersioningModal from "./EnableVersioningModal";
import UsageIcon from "../../../../icons/UsageIcon";
import AddPolicy from "../../Policies/AddPolicy";
import SetAccessPolicy from "./SetAccessPolicy";
import DeleteReplicationRule from "../ViewBucket/DeleteReplicationRule";
const styles = (theme: Theme) =>

View File

@@ -112,6 +112,8 @@ const AddTenant = ({
const ADGroupBaseDN = fields.identityProvider.ADGroupBaseDN;
const ADGroupSearchFilter = fields.identityProvider.ADGroupSearchFilter;
const ADNameAttribute = fields.identityProvider.ADNameAttribute;
const accessKeys = fields.identityProvider.accessKeys;
const secretKeys = fields.identityProvider.secretKeys;
const minioCertificates = certificates.minioCertificates;
const caCertificates = certificates.caCertificates;
const consoleCertificate = certificates.consoleCertificate;
@@ -155,11 +157,12 @@ const AddTenant = ({
const logSearchCustom = fields.configure.logSearchCustom;
const prometheusCustom = fields.configure.prometheusCustom;
const logSearchVolumeSize = fields.configure.logSearchVolumeSize;
const logSearchSelectedStorageClass = fields.configure.logSearchSelectedStorageClass;
const prometheusSelectedStorageClass = fields.configure.prometheusSelectedStorageClass;
const logSearchSelectedStorageClass =
fields.configure.logSearchSelectedStorageClass;
const prometheusSelectedStorageClass =
fields.configure.prometheusSelectedStorageClass;
const prometheusVolumeSize = fields.configure.prometheusVolumeSize;
if (addSending) {
const poolName = generatePoolName([]);
@@ -175,6 +178,8 @@ const AddTenant = ({
namespace: namespace,
access_key: "",
secret_key: "",
access_keys: [],
secret_keys: [],
enable_tls: enableTLS && enableAutoCert,
enable_console: true,
enable_prometheus: true,
@@ -217,23 +222,23 @@ const AddTenant = ({
};
}
if(logSearchCustom) {
if (logSearchCustom) {
dataSend = {
...dataSend,
logSearchConfiguration: {
storageClass: logSearchSelectedStorageClass,
storageSize: parseInt(logSearchVolumeSize),
}
},
};
}
if(prometheusCustom) {
if (prometheusCustom) {
dataSend = {
...dataSend,
prometheusConfiguration: {
storageClass: prometheusSelectedStorageClass,
storageSize: parseInt(prometheusVolumeSize),
}
},
};
}
@@ -428,41 +433,50 @@ const AddTenant = ({
};
}
if (idpSelection !== "Built-in") {
let dataIDP: any = {};
switch (idpSelection) {
case "OpenID":
dataIDP = {
oidc: {
url: openIDURL,
client_id: openIDClientID,
secret_id: openIDSecretID,
},
};
break;
case "AD":
dataIDP = {
active_directory: {
url: ADURL,
skip_tls_verification: ADSkipTLS,
server_insecure: ADServerInsecure,
username_format: "",
user_search_filter: ADUserNameFilter,
group_search_base_dn: ADGroupBaseDN,
group_search_filter: ADGroupSearchFilter,
group_name_attribute: ADNameAttribute,
},
};
break;
}
dataSend = {
...dataSend,
idp: { ...dataIDP },
};
let dataIDP: any = {};
switch (idpSelection) {
case "Built-in":
let keyarray = [];
for (let i = 0; i < accessKeys.length; i++) {
keyarray.push({
access_key: accessKeys[i],
secret_key: secretKeys[i],
});
}
dataIDP = {
keys: keyarray,
};
break;
case "OpenID":
dataIDP = {
oidc: {
url: openIDURL,
client_id: openIDClientID,
secret_id: openIDSecretID,
},
};
break;
case "AD":
dataIDP = {
active_directory: {
url: ADURL,
skip_tls_verification: ADSkipTLS,
server_insecure: ADServerInsecure,
username_format: "",
user_search_filter: ADUserNameFilter,
group_search_base_dn: ADGroupBaseDN,
group_search_filter: ADGroupSearchFilter,
group_name_attribute: ADNameAttribute,
},
};
break;
}
dataSend = {
...dataSend,
idp: { ...dataIDP },
};
api
.invoke("POST", `/api/v1/tenants`, dataSend)
.then((res) => {

View File

@@ -17,7 +17,8 @@
import React, { useEffect, useState, useCallback, Fragment } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Grid, Typography } from "@material-ui/core";
import { Grid, Typography, Button, IconButton, Icon } from "@material-ui/core";
import CasinoIcon from "@material-ui/icons/Casino";
import {
modalBasic,
wizardCommon,
@@ -32,10 +33,15 @@ import { clearValidationError } from "../../utils";
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import AddIcon from "../../../../../icons/AddIcon";
import { CreateIcon } from "../../../../../icons";
import { Casino } from "@material-ui/icons";
interface IIdentityProviderProps {
classes: any;
idpSelection: string;
accessKeys: string[];
secretKeys: string[];
openIDURL: string;
openIDClientID: string;
openIDSecretID: string;
@@ -55,6 +61,11 @@ const styles = (theme: Theme) =>
buttonContainer: {
textAlign: "right",
},
shortened: {
gridTemplateColumns: "auto auto 30px",
display: "grid",
gridGap: 20,
},
...modalBasic,
...wizardCommon,
});
@@ -62,6 +73,8 @@ const styles = (theme: Theme) =>
const IdentityProvider = ({
classes,
idpSelection,
accessKeys,
secretKeys,
openIDURL,
openIDClientID,
openIDSecretID,
@@ -78,12 +91,32 @@ const IdentityProvider = ({
const [validationErrors, setValidationErrors] = useState<any>({});
// Common
let randomKey = function (): string {
let retval = "";
let legalcharacters =
"1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (let i = 0; i < 16; i++) {
retval +=
legalcharacters[Math.floor(Math.random() * legalcharacters.length)];
}
return retval;
};
const updateField = useCallback(
(field: string, value: any) => {
updateAddField("identityProvider", field, value);
},
[updateAddField]
);
const updateUserField = (index: number, value: string) => {
const newUserField = [...accessKeys];
newUserField[index] = value;
updateField("accessKeys", newUserField);
};
const updatePwordField = (index: number, value: string) => {
const newUserField = [...secretKeys];
newUserField[index] = value;
updateField("secretKeys", newUserField);
};
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
@@ -95,10 +128,23 @@ const IdentityProvider = ({
let customIDPValidation: IValidation[] = [];
if (idpSelection === "Built-in") {
isPageValid("identityProvider", true);
setValidationErrors({});
return;
customIDPValidation = [...customIDPValidation];
for (var i = 0; i < accessKeys.length; i++) {
customIDPValidation.push({
fieldKey: `accesskey-${i.toString()}`,
required: true,
value: accessKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
customIDPValidation.push({
fieldKey: `secretkey-${i.toString()}`,
required: true,
value: secretKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
}
}
if (idpSelection === "OpenID") {
@@ -160,6 +206,8 @@ const IdentityProvider = ({
setValidationErrors(commonVal);
}, [
idpSelection,
accessKeys,
secretKeys,
openIDURL,
openIDClientID,
openIDSecretID,
@@ -170,7 +218,52 @@ const IdentityProvider = ({
ADNameAttribute,
isPageValid,
]);
let inputs = null;
if (idpSelection === "Built-in") {
inputs = accessKeys.map((element, index) => {
return (
<Fragment>
<div className={classes.shortened}>
<InputBoxWrapper
id={`accesskey-${index.toString()}`}
label={"Access Key"}
name={`accesskey-${index.toString()}`}
value={accessKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateUserField(index, e.target.value);
cleanValidation(`accesskey-${index.toString()}`);
}}
index={index}
key={`csv-accesskey-${index.toString()}`}
error={validationErrors[`accesskey-${index.toString()}`] || ""}
/>
<InputBoxWrapper
id={`secretkey-${index.toString()}`}
label={"Secret Key"}
name={`secretkey-${index.toString()}`}
value={secretKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updatePwordField(index, e.target.value);
cleanValidation(`secretkey-${index.toString()}`);
}}
index={index}
key={`csv-secretkey-${index.toString()}`}
error={validationErrors[`secretkey-${index.toString()}`] || ""}
/>
<IconButton
onClick={() => {
updateUserField(index, randomKey());
updatePwordField(index, randomKey());
}}
>
<CasinoIcon />
</IconButton>
</div>
<br />
</Fragment>
);
});
}
return (
<Fragment>
<div className={classes.headerElement}>
@@ -196,8 +289,22 @@ const IdentityProvider = ({
]}
/>
MinIO supports both OpenID and Active Directory
</Grid>
<Button
onClick={() => {
accessKeys.push("");
secretKeys.push("");
updateUserField(accessKeys.length - 1, "");
updatePwordField(secretKeys.length - 1, "");
}}
startIcon={<CreateIcon />}
className={classes.buttonList}
color="primary"
variant="contained"
>
Add accesskey/secretkey pair
</Button>
</Grid>{" "}
{idpSelection === "Built-in" && <Fragment>{inputs}</Fragment>}
{idpSelection === "OpenID" && (
<Fragment>
<Grid item xs={12}>
@@ -367,6 +474,8 @@ const IdentityProvider = ({
const mapState = (state: AppState) => ({
idpSelection: state.tenants.createTenant.fields.identityProvider.idpSelection,
accessKeys: state.tenants.createTenant.fields.identityProvider.accessKeys,
secretKeys: state.tenants.createTenant.fields.identityProvider.secretKeys,
openIDURL: state.tenants.createTenant.fields.identityProvider.openIDURL,
openIDClientID:
state.tenants.createTenant.fields.identityProvider.openIDClientID,

View File

@@ -70,10 +70,12 @@ const initialState: ITenantState = {
prometheusVolumeSize: "5",
prometheusSizeFactor: "Gi",
logSearchSelectedStorageClass: "",
prometheusSelectedStorageClass: "",
prometheusSelectedStorageClass: "",
},
identityProvider: {
idpSelection: "Built-in",
accessKeys: [""],
secretKeys: [""],
openIDURL: "",
openIDClientID: "",
openIDSecretID: "",
@@ -459,6 +461,8 @@ export function tenantsReducer(
},
identityProvider: {
idpSelection: "Built-in",
accessKeys: [""],
secretKeys: [""],
openIDURL: "",
openIDClientID: "",
openIDSecretID: "",

View File

@@ -110,6 +110,8 @@ export interface IConfigureFields {
export interface IIdentityProviderFields {
idpSelection: string;
accessKeys: string[];
secretKeys: string[];
openIDURL: string;
openIDClientID: string;
openIDSecretID: string;

View File

@@ -540,8 +540,8 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
}
ns := *tenantReq.Namespace
// if access/secret are provided, use them, else create a random pair
accessKey := RandomCharString(16)
secretKey := RandomCharString(32)
@@ -555,24 +555,48 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
tenantName := *tenantReq.Name
secretName := fmt.Sprintf("%s-secret", tenantName)
imm := true
instanceSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Labels: map[string]string{
miniov2.TenantLabel: tenantName,
var instanceSecret corev1.Secret
var users []*corev1.LocalObjectReference
if !(len(tenantReq.Idp.Keys) > 0) {
instanceSecret = corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Labels: map[string]string{
miniov2.TenantLabel: tenantName,
},
},
},
Immutable: &imm,
Data: map[string][]byte{
"accesskey": []byte(accessKey),
"secretkey": []byte(secretKey),
},
}
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(err)
Immutable: &imm,
Data: map[string][]byte{
"accesskey": []byte(accessKey),
"secretkey": []byte(secretKey),
},
}
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(err)
}
} else {
users = append(users, &corev1.LocalObjectReference{Name: secretName})
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
users = append(users, &corev1.LocalObjectReference{Name: fmt.Sprintf("%s%d", secretName, i)})
instanceSecret = corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s%d", secretName, i),
Labels: map[string]string{
miniov2.TenantLabel: tenantName,
},
},
Immutable: &imm,
Data: map[string][]byte{
"CONSOLE_ACCESS_KEY": []byte(*tenantReq.Idp.Keys[i].AccessKey),
"CONSOLE_SECRET_KEY": []byte(*tenantReq.Idp.Keys[i].SecretKey),
},
}
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(err)
}
}
}
// delete secrets created if an error occurred during tenant creation,
defer func() {
@@ -616,6 +640,9 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
},
}
idpEnabled := false
if len(tenantReq.Idp.Keys) > 0 {
minInst.Spec.Users = users
}
// Enable IDP (Active Directory) for MinIO
if tenantReq.Idp != nil && tenantReq.Idp.ActiveDirectory != nil {
url := *tenantReq.Idp.ActiveDirectory.URL
@@ -749,18 +776,36 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
if enableConsole {
// provision initial user for tenant
tenantUserAccessKey = RandomCharString(16)
tenantUserSecretKey = RandomCharString(32)
consoleUserSecretName := fmt.Sprintf("%s-user-secret", tenantName)
consoleUserSecretData := map[string][]byte{
"CONSOLE_ACCESS_KEY": []byte(tenantUserAccessKey),
"CONSOLE_SECRET_KEY": []byte(tenantUserSecretKey),
if !(len(tenantReq.Idp.Keys) > 0) {
tenantUserAccessKey = RandomCharString(16)
tenantUserSecretKey = RandomCharString(32)
consoleUserSecretName := fmt.Sprintf("%s-user-secret", tenantName)
consoleUserSecretData := map[string][]byte{
"CONSOLE_ACCESS_KEY": []byte(tenantUserAccessKey),
"CONSOLE_SECRET_KEY": []byte(tenantUserSecretKey),
}
_, err := createOrReplaceSecrets(ctx, &k8sClient, ns, []tenantSecret{{Name: consoleUserSecretName, Content: consoleUserSecretData}}, tenantName)
if err != nil {
return nil, prepareError(errorGeneric, nil, err)
}
minInst.Spec.Users = []*corev1.LocalObjectReference{{Name: consoleUserSecretName}}
} else {
tenantUserAccessKey = *tenantReq.Idp.Keys[0].AccessKey
tenantUserSecretKey = *tenantReq.Idp.Keys[0].SecretKey
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
userSecretName := fmt.Sprintf("%s-%d-user-secret", tenantName, i)
userSecretData := map[string][]byte{
"CONSOLE_ACCESS_KEY": []byte(*tenantReq.Idp.Keys[i].AccessKey),
"CONSOLE_SECRET_KEY": []byte(*tenantReq.Idp.Keys[i].SecretKey),
}
_, err := createOrReplaceSecrets(ctx, &k8sClient, ns, []tenantSecret{{Name: userSecretName, Content: userSecretData}}, tenantName)
if err != nil {
return nil, prepareError(errorGeneric, nil, err)
}
minInst.Spec.Users = append(minInst.Spec.Users, &corev1.LocalObjectReference{Name: userSecretName})
}
}
_, err := createOrReplaceSecrets(ctx, &k8sClient, ns, []tenantSecret{{Name: consoleUserSecretName, Content: consoleUserSecretData}}, tenantName)
if err != nil {
return nil, prepareError(errorGeneric, nil, err)
}
minInst.Spec.Users = []*corev1.LocalObjectReference{{Name: consoleUserSecretName}}
consoleSelector := fmt.Sprintf("%s-console", tenantName)
consoleSecretName := fmt.Sprintf("%s-secret", consoleSelector)
consoleSecretData := map[string][]byte{
@@ -962,7 +1007,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
return nil, prepareError(err)
}
// Integratrions
// Integrations
if os.Getenv("GKE_INTEGRATION") != "" {
err := gkeIntegration(clientSet, tenantName, ns, session.STSSessionToken)
if err != nil {

View File

@@ -4308,6 +4308,24 @@ func init() {
}
}
},
"keys": {
"type": "array",
"items": {
"type": "object",
"required": [
"access_key",
"secret_key"
],
"properties": {
"access_key": {
"type": "string"
},
"secret_key": {
"type": "string"
}
}
}
},
"oidc": {
"type": "object",
"required": [
@@ -9717,6 +9735,21 @@ func init() {
}
}
},
"IdpConfigurationKeysItems0": {
"type": "object",
"required": [
"access_key",
"secret_key"
],
"properties": {
"access_key": {
"type": "string"
},
"secret_key": {
"type": "string"
}
}
},
"IdpConfigurationOidc": {
"type": "object",
"required": [
@@ -11000,6 +11033,12 @@ func init() {
}
}
},
"keys": {
"type": "array",
"items": {
"$ref": "#/definitions/IdpConfigurationKeysItems0"
}
},
"oidc": {
"type": "object",
"required": [

View File

@@ -3454,6 +3454,18 @@ definitions:
type: string
secret_id:
type: string
keys:
type: array
items:
type: object
required:
- access_key
- secret_key
properties:
access_key:
type: string
secret_key:
type: string
active_directory:
type: object
required: