Operator Console bug fixes and MinIO configuration file support (#898)

- Added support for MinIO configuration file
- fix: tenant deployment with oidc integration
- fix: tenant deployment with ldap integration
- fix: certificate parsing for domains and IP addresses on security tab
- fix: console certificate upload was not working

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
Lenin Alevski
2021-08-03 17:36:26 -07:00
committed by GitHub
parent fcb74aee77
commit 56436f20e2
28 changed files with 889 additions and 375 deletions

4
go.mod
View File

@@ -22,8 +22,8 @@ require (
github.com/minio/madmin-go v1.0.17
github.com/minio/mc v0.0.0-20210626002108-cebf3318546f
github.com/minio/minio-go/v7 v7.0.13-0.20210715203016-9e713532886e
github.com/minio/operator v0.0.0-20210616045941-65f31f5f78ae
github.com/minio/operator/logsearchapi v0.0.0-20210604224119-7e256f98cf90
github.com/minio/operator v0.0.0-20210803012017-0f43eee7fd7a
github.com/minio/operator/logsearchapi v0.0.0-20210803012017-0f43eee7fd7a
github.com/minio/pkg v1.0.8
github.com/minio/selfupdate v0.3.1
github.com/mitchellh/go-homedir v1.1.0

11
go.sum
View File

@@ -469,6 +469,8 @@ github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -876,7 +878,6 @@ github.com/minio/filepath v1.0.0 h1:fvkJu1+6X+ECRA6G3+JJETj4QeAYO9sV43I79H8ubDY=
github.com/minio/filepath v1.0.0/go.mod h1:/nRZA2ldl5z6jT9/KQuvZcQlxZIMQoFFQPvEXx9T/Bw=
github.com/minio/kes v0.11.0 h1:8ma6OCVSxKT50b1uYXLJro3m7PmZtCLxBaTddQexI5k=
github.com/minio/kes v0.11.0/go.mod h1:mTF1Bv8YVEtQqF/B7Felp4tLee44Pp+dgI0rhCvgNg8=
github.com/minio/madmin-go v1.0.6/go.mod h1:BK+z4XRx7Y1v8SFWXsuLNqQqnq5BO/axJ8IDJfgyvfs=
github.com/minio/madmin-go v1.0.12/go.mod h1:BK+z4XRx7Y1v8SFWXsuLNqQqnq5BO/axJ8IDJfgyvfs=
github.com/minio/madmin-go v1.0.17 h1:VMEn4nMKf0X3uNH0u+fZcn17KSwVkQGwyER/igG556E=
github.com/minio/madmin-go v1.0.17/go.mod h1:4nl9hvLWFnwCjkLfZSsZXEHgDODa2XSG6xGlIZyQ2oA=
@@ -889,10 +890,10 @@ github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78/go.mod h1:mTh
github.com/minio/minio-go/v7 v7.0.11-0.20210607181445-e162fdb8e584/go.mod h1:WoyW+ySKAKjY98B9+7ZbI8z8S3jaxaisdcvj9TGlazA=
github.com/minio/minio-go/v7 v7.0.13-0.20210715203016-9e713532886e h1:aVnxKPpUI1gVeEf9vC+QEt8OxMXiiNMeUWcrBM62oDU=
github.com/minio/minio-go/v7 v7.0.13-0.20210715203016-9e713532886e/go.mod h1:S23iSP5/gbMwtxeY5FM71R+TkAYyzEdoNEDDwpt8yWs=
github.com/minio/operator v0.0.0-20210616045941-65f31f5f78ae h1:GONmqbjCi/KTEc1CGujnS/m1qeJeghcQ8dUBLh19qQo=
github.com/minio/operator v0.0.0-20210616045941-65f31f5f78ae/go.mod h1:8/mIXK+CFdL6VqyxRn1SwD+PEX0jsN8uqjoadaw/Np0=
github.com/minio/operator/logsearchapi v0.0.0-20210604224119-7e256f98cf90 h1:Qu6j6oE7+QNuq7Kr2DLyVYq3fqMdqFd/T8NAeNp47og=
github.com/minio/operator/logsearchapi v0.0.0-20210604224119-7e256f98cf90/go.mod h1:R+38Pf3wfm+JMiyLPb/r8OMrBm0vK2hZgUT4y4aYoSY=
github.com/minio/operator v0.0.0-20210803012017-0f43eee7fd7a h1:jvEyFZBLo1mIc5YTg+AIAieDkzoAnc9+j4yT5kZO15E=
github.com/minio/operator v0.0.0-20210803012017-0f43eee7fd7a/go.mod h1:zQqn6VGT46xlSpVXh1I/VZRv+eSgHtVu6URdg71YKX8=
github.com/minio/operator/logsearchapi v0.0.0-20210803012017-0f43eee7fd7a h1:tnyzzgWP0PXM1nrwHtlyawuBguY+6R9/yee0bezbGDY=
github.com/minio/operator/logsearchapi v0.0.0-20210803012017-0f43eee7fd7a/go.mod h1:R+38Pf3wfm+JMiyLPb/r8OMrBm0vK2hZgUT4y4aYoSY=
github.com/minio/pkg v1.0.3/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=
github.com/minio/pkg v1.0.4/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=
github.com/minio/pkg v1.0.8 h1:lWQwHSeYlvnRoPpO+wS0I4mL6c00ABxBgbGjSmjwOi4=

View File

@@ -227,9 +227,18 @@ type IdpConfigurationActiveDirectory struct {
// group search filter
GroupSearchFilter string `json:"group_search_filter,omitempty"`
// lookup bind dn
LookupBindDn string `json:"lookup_bind_dn,omitempty"`
// lookup bind password
LookupBindPassword string `json:"lookup_bind_password,omitempty"`
// server insecure
ServerInsecure bool `json:"server_insecure,omitempty"`
// server start tls
ServerStartTLS bool `json:"server_start_tls,omitempty"`
// skip tls verification
SkipTLSVerification bool `json:"skip_tls_verification,omitempty"`
@@ -237,13 +246,20 @@ type IdpConfigurationActiveDirectory struct {
// Required: true
URL *string `json:"url"`
// user search filter
// Required: true
UserSearchFilter *string `json:"user_search_filter"`
// user dn search base dn
UserDnSearchBaseDn string `json:"user_dn_search_base_dn,omitempty"`
// user dn search filter
UserDnSearchFilter string `json:"user_dn_search_filter,omitempty"`
// user dns
UserDNS []string `json:"user_dns"`
// username format
// Required: true
UsernameFormat *string `json:"username_format"`
UsernameFormat string `json:"username_format,omitempty"`
// username search filter
UsernameSearchFilter string `json:"username_search_filter,omitempty"`
}
// Validate validates this idp configuration active directory
@@ -254,14 +270,6 @@ func (m *IdpConfigurationActiveDirectory) Validate(formats strfmt.Registry) erro
res = append(res, err)
}
if err := m.validateUserSearchFilter(formats); err != nil {
res = append(res, err)
}
if err := m.validateUsernameFormat(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -277,24 +285,6 @@ func (m *IdpConfigurationActiveDirectory) validateURL(formats strfmt.Registry) e
return nil
}
func (m *IdpConfigurationActiveDirectory) validateUserSearchFilter(formats strfmt.Registry) error {
if err := validate.Required("active_directory"+"."+"user_search_filter", "body", m.UserSearchFilter); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationActiveDirectory) validateUsernameFormat(formats strfmt.Registry) error {
if err := validate.Required("active_directory"+"."+"username_format", "body", m.UsernameFormat); err != nil {
return err
}
return nil
}
// ContextValidate validates this idp configuration active directory based on context it is used
func (m *IdpConfigurationActiveDirectory) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
@@ -396,10 +386,24 @@ func (m *IdpConfigurationKeysItems0) UnmarshalBinary(b []byte) error {
// swagger:model IdpConfigurationOidc
type IdpConfigurationOidc struct {
// callback url
CallbackURL string `json:"callback_url,omitempty"`
// claim name
// Required: true
ClaimName *string `json:"claim_name"`
// client id
// Required: true
ClientID *string `json:"client_id"`
// configuration url
// Required: true
ConfigurationURL *string `json:"configuration_url"`
// scopes
Scopes string `json:"scopes,omitempty"`
// secret id
// Required: true
SecretID *string `json:"secret_id"`
@@ -413,10 +417,18 @@ type IdpConfigurationOidc struct {
func (m *IdpConfigurationOidc) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateClaimName(formats); err != nil {
res = append(res, err)
}
if err := m.validateClientID(formats); err != nil {
res = append(res, err)
}
if err := m.validateConfigurationURL(formats); err != nil {
res = append(res, err)
}
if err := m.validateSecretID(formats); err != nil {
res = append(res, err)
}
@@ -431,6 +443,15 @@ func (m *IdpConfigurationOidc) Validate(formats strfmt.Registry) error {
return nil
}
func (m *IdpConfigurationOidc) validateClaimName(formats strfmt.Registry) error {
if err := validate.Required("oidc"+"."+"claim_name", "body", m.ClaimName); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationOidc) validateClientID(formats strfmt.Registry) error {
if err := validate.Required("oidc"+"."+"client_id", "body", m.ClientID); err != nil {
@@ -440,6 +461,15 @@ func (m *IdpConfigurationOidc) validateClientID(formats strfmt.Registry) error {
return nil
}
func (m *IdpConfigurationOidc) validateConfigurationURL(formats strfmt.Registry) error {
if err := validate.Required("oidc"+"."+"configuration_url", "body", m.ConfigurationURL); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationOidc) validateSecretID(formats strfmt.Registry) error {
if err := validate.Required("oidc"+"."+"secret_id", "body", m.SecretID); err != nil {

View File

@@ -66,8 +66,8 @@ type Tenant struct {
// idp ad enabled
IdpAdEnabled bool `json:"idpAdEnabled,omitempty"`
// idp oic enabled
IdpOicEnabled bool `json:"idpOicEnabled,omitempty"`
// idp oidc enabled
IdpOidcEnabled bool `json:"idpOidcEnabled,omitempty"`
// image
Image string `json:"image,omitempty"`

View File

@@ -2489,9 +2489,7 @@ func init() {
"active_directory": {
"type": "object",
"required": [
"url",
"username_format",
"user_search_filter"
"url"
],
"properties": {
"group_name_attribute": {
@@ -2503,20 +2501,41 @@ func init() {
"group_search_filter": {
"type": "string"
},
"lookup_bind_dn": {
"type": "string"
},
"lookup_bind_password": {
"type": "string"
},
"server_insecure": {
"type": "boolean"
},
"server_start_tls": {
"type": "boolean"
},
"skip_tls_verification": {
"type": "boolean"
},
"url": {
"type": "string"
},
"user_search_filter": {
"user_dn_search_base_dn": {
"type": "string"
},
"user_dn_search_filter": {
"type": "string"
},
"user_dns": {
"type": "array",
"items": {
"type": "string"
}
},
"username_format": {
"type": "string"
},
"username_search_filter": {
"type": "string"
}
}
},
@@ -2541,14 +2560,28 @@ func init() {
"oidc": {
"type": "object",
"required": [
"configuration_url",
"url",
"client_id",
"secret_id"
"secret_id",
"claim_name"
],
"properties": {
"callback_url": {
"type": "string"
},
"claim_name": {
"type": "string"
},
"client_id": {
"type": "string"
},
"configuration_url": {
"type": "string"
},
"scopes": {
"type": "string"
},
"secret_id": {
"type": "string"
},
@@ -4230,7 +4263,7 @@ func init() {
"idpAdEnabled": {
"type": "boolean"
},
"idpOicEnabled": {
"idpOidcEnabled": {
"type": "boolean"
},
"image": {
@@ -6441,9 +6474,7 @@ func init() {
"IdpConfigurationActiveDirectory": {
"type": "object",
"required": [
"url",
"username_format",
"user_search_filter"
"url"
],
"properties": {
"group_name_attribute": {
@@ -6455,20 +6486,41 @@ func init() {
"group_search_filter": {
"type": "string"
},
"lookup_bind_dn": {
"type": "string"
},
"lookup_bind_password": {
"type": "string"
},
"server_insecure": {
"type": "boolean"
},
"server_start_tls": {
"type": "boolean"
},
"skip_tls_verification": {
"type": "boolean"
},
"url": {
"type": "string"
},
"user_search_filter": {
"user_dn_search_base_dn": {
"type": "string"
},
"user_dn_search_filter": {
"type": "string"
},
"user_dns": {
"type": "array",
"items": {
"type": "string"
}
},
"username_format": {
"type": "string"
},
"username_search_filter": {
"type": "string"
}
}
},
@@ -6490,14 +6542,28 @@ func init() {
"IdpConfigurationOidc": {
"type": "object",
"required": [
"configuration_url",
"url",
"client_id",
"secret_id"
"secret_id",
"claim_name"
],
"properties": {
"callback_url": {
"type": "string"
},
"claim_name": {
"type": "string"
},
"client_id": {
"type": "string"
},
"configuration_url": {
"type": "string"
},
"scopes": {
"type": "string"
},
"secret_id": {
"type": "string"
},
@@ -8073,9 +8139,7 @@ func init() {
"active_directory": {
"type": "object",
"required": [
"url",
"username_format",
"user_search_filter"
"url"
],
"properties": {
"group_name_attribute": {
@@ -8087,20 +8151,41 @@ func init() {
"group_search_filter": {
"type": "string"
},
"lookup_bind_dn": {
"type": "string"
},
"lookup_bind_password": {
"type": "string"
},
"server_insecure": {
"type": "boolean"
},
"server_start_tls": {
"type": "boolean"
},
"skip_tls_verification": {
"type": "boolean"
},
"url": {
"type": "string"
},
"user_search_filter": {
"user_dn_search_base_dn": {
"type": "string"
},
"user_dn_search_filter": {
"type": "string"
},
"user_dns": {
"type": "array",
"items": {
"type": "string"
}
},
"username_format": {
"type": "string"
},
"username_search_filter": {
"type": "string"
}
}
},
@@ -8113,14 +8198,28 @@ func init() {
"oidc": {
"type": "object",
"required": [
"configuration_url",
"url",
"client_id",
"secret_id"
"secret_id",
"claim_name"
],
"properties": {
"callback_url": {
"type": "string"
},
"claim_name": {
"type": "string"
},
"client_id": {
"type": "string"
},
"configuration_url": {
"type": "string"
},
"scopes": {
"type": "string"
},
"secret_id": {
"type": "string"
},
@@ -9667,7 +9766,7 @@ func init() {
"idpAdEnabled": {
"type": "boolean"
},
"idpOicEnabled": {
"idpOidcEnabled": {
"type": "boolean"
},
"image": {

View File

@@ -33,6 +33,10 @@ import (
"strings"
"time"
"github.com/minio/console/pkg/auth/ldap"
"github.com/minio/console/pkg/auth/idp/oauth2"
"github.com/dustin/go-humanize"
"github.com/minio/console/restapi"
@@ -435,8 +439,9 @@ func getTenantInfo(tenant *miniov2.Tenant) *models.Tenant {
func getTenantDetailsResponse(session *models.Principal, params operator_api.TenantDetailsParams) (*models.Tenant, *models.Error) {
// 5 seconds timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
//ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
//defer cancel()
ctx := context.Background()
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
if err != nil {
return nil, prepareError(err)
@@ -453,14 +458,6 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
info := getTenantInfo(minTenant)
// detect if AD is enabled
adEnabled := false
for _, env := range minTenant.Spec.Env {
if env.Name == "MINIO_IDENTITY_LDAP_SERVER_ADDR" && env.Value != "" {
adEnabled = true
}
}
// get Kubernetes Client
clientSet, err := cluster.K8sClient(session.STSSessionToken)
if err != nil {
@@ -471,34 +468,51 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
client: clientSet,
}
// detect if OpenID is enabled
tenantConfiguration := map[string][]byte{}
oicEnabled := false
consoleSelector := fmt.Sprintf("%s-console", minTenant.Name)
consoleSecretName := fmt.Sprintf("%s-secret", consoleSelector)
consoleSecret, err := clientSet.CoreV1().Secrets(minTenant.Namespace).Get(ctx, consoleSecretName, metav1.GetOptions{})
// we can tolerate not getting this secret
if err != nil {
restapi.LogError("unable to fetch existing secrets for %s: %v", minTenant.Name, err)
for _, config := range minTenant.GetEnvVars() {
tenantConfiguration[config.Name] = []byte(config.Value)
}
if consoleSecret != nil {
if _, ok := consoleSecret.Data["CONSOLE_IDP_URL"]; ok {
oicEnabled = true
if minTenant.HasCredsSecret() {
minioSecret, err := clientSet.CoreV1().Secrets(minTenant.Namespace).Get(ctx, minTenant.Spec.CredsSecret.Name, metav1.GetOptions{})
// we can tolerate not getting this secret
if err != nil {
restapi.LogError("unable to fetch existing secrets for %s: %v", minTenant.Name, err)
}
configFromCredsSecret := minioSecret.Data
for key, val := range configFromCredsSecret {
tenantConfiguration[key] = val
}
}
if minTenant.HasConsoleEnabled() {
for _, env := range minTenant.Spec.Console.Env {
if env.Name == "CONSOLE_IDP_URL" {
oicEnabled = true
if minTenant.HasConfigurationSecret() {
minioConfigurationSecret, err := clientSet.CoreV1().Secrets(minTenant.Namespace).Get(ctx, minTenant.Spec.Configuration.Name, metav1.GetOptions{})
if err == nil {
configFromFile := miniov2.ParseRawConfiguration(minioConfigurationSecret.Data["config.env"])
for key, val := range configFromFile {
tenantConfiguration[key] = val
}
}
}
// detect if AD/LDAP is enabled
ldapEnabled := false
if string(tenantConfiguration["MINIO_IDENTITY_LDAP_SERVER_ADDR"]) != "" {
ldapEnabled = true
}
// detect if OpenID is enabled
oidcEnabled := false
if string(tenantConfiguration["MINIO_IDENTITY_OPENID_CONFIG_URL"]) != "" {
oidcEnabled = true
}
info.LogEnabled = minTenant.HasLogEnabled()
info.MonitoringEnabled = minTenant.HasPrometheusEnabled()
info.EncryptionEnabled = minTenant.HasKESEnabled()
info.IdpAdEnabled = adEnabled
info.IdpOicEnabled = oicEnabled
info.IdpAdEnabled = ldapEnabled
info.IdpOidcEnabled = oidcEnabled
info.MinioTLS = minTenant.TLS()
info.ConsoleTLS = minTenant.AutoCert() || minTenant.ConsoleExternalCert()
info.ConsoleEnabled = minTenant.HasConsoleEnabled()
@@ -607,10 +621,21 @@ func parseTenantCertificates(ctx context.Context, clientSet K8sClientI, namespac
if err != nil {
return nil, err
}
domains := []string{}
// append certificate domain names
if len(cert.DNSNames) > 0 {
domains = append(domains, cert.DNSNames...)
}
// append certificate IPs
if len(cert.IPAddresses) > 0 {
for _, ip := range cert.IPAddresses {
domains = append(domains, ip.String())
}
}
certificates = append(certificates, &models.CertificateInfo{
SerialNumber: cert.SerialNumber.String(),
Name: secret.Name,
Domains: cert.DNSNames,
Domains: domains,
Expiry: cert.NotAfter.String(),
})
}
@@ -656,7 +681,8 @@ func getTenantSecurity(ctx context.Context, clientSet K8sClientI, tenant *miniov
func getTenantSecurityResponse(session *models.Principal, params operator_api.TenantSecurityParams) (*models.TenantSecurityResponse, *models.Error) {
// 5 seconds timeout
ctx := context.Background()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
//ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
//defer cancel()
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
@@ -989,6 +1015,8 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
var instanceSecret corev1.Secret
var users []*corev1.LocalObjectReference
var tenantConfigurationENV string
// Create the secret for the root credentials
secretName := fmt.Sprintf("%s-secret", tenantName)
instanceSecret = corev1.Secret{
@@ -1000,10 +1028,14 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
},
Immutable: &imm,
Data: map[string][]byte{
"accesskey": []byte(accessKey),
"secretkey": []byte(secretKey),
"accesskey": []byte(""),
"secretkey": []byte(""),
},
}
tenantConfigurationENV += fmt.Sprintf("export MINIO_ROOT_USER=\"%s\"\n", accessKey)
tenantConfigurationENV += fmt.Sprintf("export MINIO_ROOT_PASSWORD=\"%s\"\n", secretKey)
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(err)
@@ -1023,16 +1055,12 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
}
}()
var environmentVariables []corev1.EnvVar
// Check the Erasure Coding Parity for validity and pass it to Tenant
if tenantReq.ErasureCodingParity > 0 {
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
return nil, prepareError(errorInvalidErasureCodingValue)
}
environmentVariables = append(environmentVariables, corev1.EnvVar{
Name: "MINIO_STORAGE_CLASS_STANDARD",
Value: fmt.Sprintf("EC:%d", tenantReq.ErasureCodingParity),
})
tenantConfigurationENV += fmt.Sprintf("export MINIO_STORAGE_CLASS_STANDARD=\"%s\"\n", fmt.Sprintf("EC:%d", tenantReq.ErasureCodingParity))
}
//Construct a MinIO Instance with everything we are getting from parameters
@@ -1047,87 +1075,120 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
CredsSecret: &corev1.LocalObjectReference{
Name: secretName,
},
Env: environmentVariables,
},
}
idpEnabled := false
// Enable IDP (Active Directory) for MinIO
if tenantReq.Idp != nil && tenantReq.Idp.ActiveDirectory != nil {
url := *tenantReq.Idp.ActiveDirectory.URL
userNameFormat := *tenantReq.Idp.ActiveDirectory.UsernameFormat
userSearchFilter := *tenantReq.Idp.ActiveDirectory.UserSearchFilter
tlsSkipVerify := tenantReq.Idp.ActiveDirectory.SkipTLSVerification
serverInsecure := tenantReq.Idp.ActiveDirectory.ServerInsecure
groupSearchDN := tenantReq.Idp.ActiveDirectory.GroupSearchBaseDn
groupSearchFilter := tenantReq.Idp.ActiveDirectory.GroupSearchFilter
groupNameAttribute := tenantReq.Idp.ActiveDirectory.GroupNameAttribute
if url != "" && userNameFormat != "" && userSearchFilter != "" {
// CONSOLE_LDAP_ENABLED
idpEnabled = true
minInst.Spec.Env = append(minInst.Spec.Env, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_SERVER_ADDR",
Value: userNameFormat,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_USERNAME_FORMAT",
Value: userNameFormat,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_USERNAME_SEARCH_FILTER",
Value: userSearchFilter,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_USERNAME_SEARCH_FILTER",
Value: userSearchFilter,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN",
Value: groupSearchDN,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER",
Value: groupSearchFilter,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE",
Value: groupNameAttribute,
})
if tenantReq.Idp != nil {
// Enable IDP (Active Directory) for MinIO
if tenantReq.Idp.ActiveDirectory != nil {
serverAddress := *tenantReq.Idp.ActiveDirectory.URL
userNameFormat := tenantReq.Idp.ActiveDirectory.UsernameFormat
userNameSearchFilter := tenantReq.Idp.ActiveDirectory.UsernameSearchFilter
groupNameAttribute := tenantReq.Idp.ActiveDirectory.GroupNameAttribute
tlsSkipVerify := tenantReq.Idp.ActiveDirectory.SkipTLSVerification
serverInsecure := tenantReq.Idp.ActiveDirectory.ServerInsecure
lookupBindDN := tenantReq.Idp.ActiveDirectory.LookupBindDn
lookupBindPassword := tenantReq.Idp.ActiveDirectory.LookupBindPassword
userDNSearchBaseDN := tenantReq.Idp.ActiveDirectory.UserDnSearchBaseDn
userDNSearchFilter := tenantReq.Idp.ActiveDirectory.UserDnSearchFilter
groupSearchBaseDN := tenantReq.Idp.ActiveDirectory.GroupSearchBaseDn
groupSearchFilter := tenantReq.Idp.ActiveDirectory.GroupSearchFilter
serverStartTLS := tenantReq.Idp.ActiveDirectory.ServerStartTLS
// LDAP Server
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_SERVER_ADDR=\"%s\"\n", serverAddress)
if tlsSkipVerify {
minInst.Spec.Env = append(minInst.Spec.Env, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY",
Value: "on",
})
tenantConfigurationENV += "export MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY=on\n"
}
if serverInsecure {
minInst.Spec.Env = append(minInst.Spec.Env, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_SERVER_INSECURE",
Value: "on",
})
tenantConfigurationENV += "export MINIO_IDENTITY_LDAP_SERVER_INSECURE=on\n"
}
if serverStartTLS {
tenantConfigurationENV += "export MINIO_IDENTITY_LDAP_SERVER_STARTTLS=on\n"
}
}
}
// Create the secret any built-in user passed if no external IDP was configured
if tenantReq.Idp != nil && len(tenantReq.Idp.Keys) > 0 && tenantReq.Idp.ActiveDirectory == nil && tenantReq.Idp.Oidc == nil {
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
userSecretName := fmt.Sprintf("%s-user-%d", tenantName, i)
users = append(users, &corev1.LocalObjectReference{Name: userSecretName})
userSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: userSecretName,
Labels: map[string]string{
miniov2.TenantLabel: tenantName,
// LDAP Username
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_USERNAME_FORMAT=\"%s\"\n", userNameFormat)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_USERNAME_SEARCH_FILTER=\"%s\"\n", userNameSearchFilter)
// LDAP Lookup
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN=\"%s\"\n", lookupBindDN)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD=\"%s\"\n", lookupBindPassword)
// LDAP User DN
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN=\"%s\"\n", userDNSearchBaseDN)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER=\"%s\"\n", userDNSearchFilter)
// LDAP Group
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE=\"%s\"\n", groupNameAttribute)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN=\"%s\"\n", groupSearchBaseDN)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER=\"%s\"\n", groupSearchFilter)
// Attach the list of LDAP user DNs that will be administrator for the Tenant
for i, userDN := range tenantReq.Idp.ActiveDirectory.UserDNS {
userSecretName := fmt.Sprintf("%s-user-%d", tenantName, i)
users = append(users, &corev1.LocalObjectReference{Name: userSecretName})
userSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: userSecretName,
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),
},
Immutable: &imm,
Data: map[string][]byte{
"CONSOLE_ACCESS_KEY": []byte(userDN),
},
}
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(err)
}
}
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(err)
// attach the users to the tenant
minInst.Spec.Users = users
} else if tenantReq.Idp.Oidc != nil {
// Enable IDP (OIDC) for MinIO
url := *tenantReq.Idp.Oidc.ConfigurationURL
clientID := *tenantReq.Idp.Oidc.ClientID
secretID := *tenantReq.Idp.Oidc.SecretID
claimName := *tenantReq.Idp.Oidc.ClaimName
scopes := tenantReq.Idp.Oidc.Scopes
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_OPENID_CONFIG_URL=\"%s\"\n", url)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_OPENID_CLIENT_ID=\"%s\"\n", clientID)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_OPENID_CLIENT_SECRET=\"%s\"\n", secretID)
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_OPENID_CLAIM_NAME=\"%s\"\n", claimName)
if scopes == "" {
scopes = "openid,profile,email"
}
tenantConfigurationENV += fmt.Sprintf("export MINIO_IDENTITY_OPENID_SCOPES=\"%s\"\n", scopes)
} else if len(tenantReq.Idp.Keys) > 0 {
// Create the secret any built-in user passed if no external IDP was configured
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
userSecretName := fmt.Sprintf("%s-user-%d", tenantName, i)
users = append(users, &corev1.LocalObjectReference{Name: userSecretName})
userSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: userSecretName,
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, &userSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(err)
}
}
// attach the users to the tenant
minInst.Spec.Users = users
}
// attach the users to the tenant
minInst.Spec.Users = users
}
isEncryptionEnabled := false
@@ -1225,7 +1286,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
consoleSecretData[restapi.ConsoleSubnetLicense] = []byte(license)
}
imm := true
instanceSecret := corev1.Secret{
consoleSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: consoleSecretName,
Labels: map[string]string{
@@ -1262,28 +1323,35 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
// If IDP is not already enabled via LDAP (Active Directory) and OIDC configuration is present then
// enable oidc for console
if !idpEnabled && tenantReq.Idp != nil && tenantReq.Idp.Oidc != nil {
url := *tenantReq.Idp.Oidc.URL
clientID := *tenantReq.Idp.Oidc.ClientID
secretID := *tenantReq.Idp.Oidc.SecretID
if url != "" && clientID != "" && secretID != "" {
instanceSecret.Data["CONSOLE_IDP_URL"] = []byte(url)
instanceSecret.Data["CONSOLE_IDP_CLIENT_ID"] = []byte(clientID)
instanceSecret.Data["CONSOLE_IDP_SECRET"] = []byte(secretID)
consoleScheme := "http"
consolePort := 9090
// If Console will be deployed with TLS enabled (using AutoCert or External certificates)
if consoleHasTLS {
consoleScheme = "https"
consolePort = 9443
if tenantReq.Idp != nil {
if tenantReq.Idp.ActiveDirectory != nil {
consoleSecret.Data[ldap.ConsoleLDAPEnabled] = []byte("on")
} else if tenantReq.Idp.Oidc != nil {
url := *tenantReq.Idp.Oidc.URL
clientID := *tenantReq.Idp.Oidc.ClientID
secretID := *tenantReq.Idp.Oidc.SecretID
callbackURL := tenantReq.Idp.Oidc.CallbackURL
if url != "" && clientID != "" && secretID != "" {
consoleSecret.Data[oauth2.ConsoleIdpURL] = []byte(url)
consoleSecret.Data[oauth2.ConsoleIdpClientID] = []byte(clientID)
consoleSecret.Data[oauth2.ConsoleIdpSecret] = []byte(secretID)
consoleScheme := "http"
consolePort := miniov2.ConsolePort
// If Console will be deployed with TLS enabled (using AutoCert or External certificates)
if consoleHasTLS {
consoleScheme = "https"
consolePort = miniov2.ConsoleTLSPort
}
// default callback url is https://localhost:9443/oauth_callback
consoleSecret.Data[oauth2.ConsoleIdpCallbackURL] = []byte(fmt.Sprintf("%s://localhost:%d/oauth_callback", consoleScheme, consolePort))
if callbackURL != "" {
consoleSecret.Data[oauth2.ConsoleIdpCallbackURL] = []byte(callbackURL)
}
}
// https://[HOSTNAME]:9443 will be replaced by javascript in the browser to use the actual hostname
// assigned to Console, eg: https://localhost:9443
instanceSecret.Data["CONSOLE_IDP_CALLBACK"] = []byte(fmt.Sprintf("%s://[HOSTNAME]:%d/oauth_callback", consoleScheme, consolePort))
}
}
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &consoleSecret, metav1.CreateOptions{})
if err != nil {
return nil, prepareError(restapi.ErrorGeneric)
}
@@ -1463,13 +1531,26 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
}
// expose services
if tenantReq.ExposeMinio || tenantReq.ExposeConsole {
minInst.Spec.ExposeServices = &miniov2.ExposeServices{
MinIO: tenantReq.ExposeMinio,
Console: tenantReq.ExposeConsole,
}
minInst.Spec.ExposeServices = &miniov2.ExposeServices{
MinIO: tenantReq.ExposeMinio,
Console: tenantReq.ExposeConsole,
}
// write tenant configuration to secret that contains config.env
tenantConfigurationName := fmt.Sprintf("%s-env-configuration", tenantName)
_, err = createOrReplaceSecrets(ctx, &k8sClient, ns, []tenantSecret{
{
Name: tenantConfigurationName,
Content: map[string][]byte{
"config.env": []byte(tenantConfigurationENV),
},
},
}, tenantName)
if err != nil {
return nil, prepareError(restapi.ErrorGeneric, nil, err)
}
minInst.Spec.Configuration = &corev1.LocalObjectReference{Name: tenantConfigurationName}
opClient, err := cluster.OperatorClient(session.STSSessionToken)
if err != nil {
return nil, prepareError(err)

View File

@@ -1,25 +1,25 @@
{
"files": {
"main.css": "/static/css/main.8cfac526.chunk.css",
"main.js": "/static/js/main.68952acf.chunk.js",
"main.js.map": "/static/js/main.68952acf.chunk.js.map",
"main.js": "/static/js/main.ab419c40.chunk.js",
"main.js.map": "/static/js/main.ab419c40.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.43a31377.js",
"runtime-main.js.map": "/static/js/runtime-main.43a31377.js.map",
"static/css/2.60e04a19.chunk.css": "/static/css/2.60e04a19.chunk.css",
"static/js/2.d3eeac57.chunk.js": "/static/js/2.d3eeac57.chunk.js",
"static/js/2.d3eeac57.chunk.js.map": "/static/js/2.d3eeac57.chunk.js.map",
"static/js/2.c9598ac3.chunk.js": "/static/js/2.c9598ac3.chunk.js",
"static/js/2.c9598ac3.chunk.js.map": "/static/js/2.c9598ac3.chunk.js.map",
"index.html": "/index.html",
"static/css/2.60e04a19.chunk.css.map": "/static/css/2.60e04a19.chunk.css.map",
"static/css/main.8cfac526.chunk.css.map": "/static/css/main.8cfac526.chunk.css.map",
"static/js/2.d3eeac57.chunk.js.LICENSE.txt": "/static/js/2.d3eeac57.chunk.js.LICENSE.txt",
"static/js/2.c9598ac3.chunk.js.LICENSE.txt": "/static/js/2.c9598ac3.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.43a31377.js",
"static/css/2.60e04a19.chunk.css",
"static/js/2.d3eeac57.chunk.js",
"static/js/2.c9598ac3.chunk.js",
"static/css/main.8cfac526.chunk.css",
"static/js/main.68952acf.chunk.js"
"static/js/main.ab419c40.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 href="/styles/root-styles.css" 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.60e04a19.chunk.css" rel="stylesheet"><link href="/static/css/main.8cfac526.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="loader-block"><svg class="loader-svg-container" viewBox="22 22 44 44"><circle class="loader-style MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate" cx="44" cy="44" r="20.2" fill="none" stroke-width="3.6"></circle></svg></div></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.d3eeac57.chunk.js"></script><script src="/static/js/main.68952acf.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 href="/styles/root-styles.css" 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.60e04a19.chunk.css" rel="stylesheet"><link href="/static/css/main.8cfac526.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="loader-block"><svg class="loader-svg-container" viewBox="22 22 44 44"><circle class="loader-style MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate" cx="44" cy="44" r="20.2" fill="none" stroke-width="3.6"></circle></svg></div></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.c9598ac3.chunk.js"></script><script src="/static/js/main.ab419c40.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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -256,10 +256,16 @@ export interface IActiveDirectoryConfiguration {
url: string;
skip_tls_verification: boolean;
server_insecure: boolean;
user_search_filter: string;
server_start_tls: boolean;
username_search_filter: string;
group_Search_base_dn: string;
group_search_filter: string;
group_name_attribute: string;
user_dns: string[];
lookup_bind_dn: string;
lookup_bind_password: string;
user_dn_search_base_dn: string;
user_dn_search_filter: string;
}
export interface IStorageDistribution {

View File

@@ -103,15 +103,28 @@ const AddTenant = ({
const exposeConsole = fields.configure.exposeConsole;
const idpSelection = fields.identityProvider.idpSelection;
const openIDURL = fields.identityProvider.openIDURL;
const openIDConfigurationURL =
fields.identityProvider.openIDConfigurationURL;
const openIDClientID = fields.identityProvider.openIDClientID;
const openIDClaimName = fields.identityProvider.openIDClaimName;
const openIDCallbackURL = fields.identityProvider.openIDCallbackURL;
const openIDScopes = fields.identityProvider.openIDScopes;
const openIDSecretID = fields.identityProvider.openIDSecretID;
const ADURL = fields.identityProvider.ADURL;
const ADSkipTLS = fields.identityProvider.ADSkipTLS;
const ADServerInsecure = fields.identityProvider.ADServerInsecure;
const ADUserNameFilter = fields.identityProvider.ADUserNameFilter;
const ADGroupBaseDN = fields.identityProvider.ADGroupBaseDN;
const ADUserNameSearchFilter =
fields.identityProvider.ADUserNameSearchFilter;
const ADGroupSearchBaseDN = fields.identityProvider.ADGroupSearchBaseDN;
const ADGroupSearchFilter = fields.identityProvider.ADGroupSearchFilter;
const ADNameAttribute = fields.identityProvider.ADNameAttribute;
const ADGroupNameAttribute = fields.identityProvider.ADGroupNameAttribute;
const ADUserDNs = fields.identityProvider.ADUserDNs;
const ADUserNameFormat = fields.identityProvider.ADUserNameFormat;
const ADLookupBindDN = fields.identityProvider.ADLookupBindDN;
const ADLookupBindPassword = fields.identityProvider.ADLookupBindPassword;
const ADUserDNSearchBaseDN = fields.identityProvider.ADUserDNSearchBaseDN;
const ADUserDNSearchFilter = fields.identityProvider.ADUserDNSearchFilter;
const ADServerStartTLS = fields.identityProvider.ADServerStartTLS;
const accessKeys = fields.identityProvider.accessKeys;
const secretKeys = fields.identityProvider.secretKeys;
const minioCertificates = certificates.minioCertificates;
@@ -503,8 +516,12 @@ const AddTenant = ({
dataIDP = {
oidc: {
url: openIDURL,
configuration_url: openIDConfigurationURL,
client_id: openIDClientID,
secret_id: openIDSecretID,
claim_name: openIDClaimName,
callback_url: openIDCallbackURL,
scopes: openIDScopes,
},
};
break;
@@ -514,11 +531,17 @@ const AddTenant = ({
url: ADURL,
skip_tls_verification: ADSkipTLS,
server_insecure: ADServerInsecure,
username_format: "",
user_search_filter: ADUserNameFilter,
group_search_base_dn: ADGroupBaseDN,
username_format: ADUserNameFormat,
username_search_filter: ADUserNameSearchFilter,
group_search_base_dn: ADGroupSearchBaseDN,
group_search_filter: ADGroupSearchFilter,
group_name_attribute: ADNameAttribute,
group_name_attribute: ADGroupNameAttribute,
user_dns: ADUserDNs,
lookup_bind_dn: ADLookupBindDN,
lookup_bind_password: ADLookupBindPassword,
user_dn_search_base_dn: ADUserDNSearchBaseDN,
user_dn_search_filter: ADUserDNSearchFilter,
server_start_tls: ADServerStartTLS,
},
};
break;
@@ -528,7 +551,6 @@ const AddTenant = ({
...dataSend,
idp: { ...dataIDP },
};
api
.invoke("POST", `/api/v1/tenants`, dataSend)
.then((res) => {

View File

@@ -42,15 +42,26 @@ interface IIdentityProviderProps {
accessKeys: string[];
secretKeys: string[];
openIDURL: string;
openIDConfigurationURL: string;
openIDClientID: string;
openIDSecretID: string;
openIDCallbackURL: string;
openIDClaimName: string;
openIDScopes: string;
ADURL: string;
ADSkipTLS: boolean;
ADServerInsecure: boolean;
ADUserNameFilter: string;
ADGroupBaseDN: string;
ADUserNameSearchFilter: string;
ADGroupSearchBaseDN: string;
ADGroupSearchFilter: string;
ADNameAttribute: string;
ADGroupNameAttribute: string;
ADUserDNs: string[];
ADUserNameFormat: string;
ADLookupBindDN: string;
ADLookupBindPassword: string;
ADUserDNSearchBaseDN: string;
ADUserDNSearchFilter: string;
ADServerStartTLS: boolean;
updateAddField: typeof updateAddField;
isPageValid: typeof isPageValid;
}
@@ -82,15 +93,26 @@ const IdentityProvider = ({
accessKeys,
secretKeys,
openIDURL,
openIDConfigurationURL,
openIDClientID,
openIDSecretID,
openIDCallbackURL,
openIDClaimName,
openIDScopes,
ADURL,
ADSkipTLS,
ADServerInsecure,
ADUserNameFilter,
ADGroupBaseDN,
ADUserNameSearchFilter,
ADGroupSearchBaseDN,
ADGroupSearchFilter,
ADNameAttribute,
ADGroupNameAttribute,
ADUserDNs,
ADUserNameFormat,
ADLookupBindDN,
ADLookupBindPassword,
ADUserDNSearchBaseDN,
ADUserDNSearchFilter,
ADServerStartTLS,
updateAddField,
isPageValid,
}: IIdentityProviderProps) => {
@@ -112,7 +134,11 @@ const IdentityProvider = ({
newUserField[index] = value;
updateField("secretKeys", newUserField);
};
const updateADUserField = (index: number, value: string) => {
const newADUserDNsField = [...ADUserDNs];
newADUserDNsField[index] = value;
updateField("ADUserDNs", newADUserDNsField);
};
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
@@ -150,6 +176,11 @@ const IdentityProvider = ({
required: true,
value: openIDURL,
},
{
fieldKey: "openID_CONFIGURATION_URL",
required: true,
value: openIDConfigurationURL,
},
{
fieldKey: "openID_clientID",
required: true,
@@ -160,6 +191,11 @@ const IdentityProvider = ({
required: true,
value: openIDSecretID,
},
{
fieldKey: "openID_claimName",
required: true,
value: openIDClaimName,
},
];
}
@@ -171,27 +207,15 @@ const IdentityProvider = ({
required: true,
value: ADURL,
},
{
fieldKey: "ad_userNameFilter",
required: true,
value: ADUserNameFilter,
},
{
fieldKey: "ad_groupBaseDN",
required: true,
value: ADGroupBaseDN,
},
{
fieldKey: "ad_groupSearchFilter",
required: true,
value: ADGroupSearchFilter,
},
{
fieldKey: "ad_nameAttribute",
required: true,
value: ADNameAttribute,
},
];
// validate user DNs
for (let i = 0; i < ADUserDNs.length; i++) {
customIDPValidation.push({
fieldKey: `ad-userdn-${i.toString()}`,
required: true,
value: ADUserDNs[i],
});
}
}
const commonVal = commonFormValidation(customIDPValidation);
@@ -207,10 +231,11 @@ const IdentityProvider = ({
openIDClientID,
openIDSecretID,
ADURL,
ADUserNameFilter,
ADGroupBaseDN,
ADUserNameSearchFilter,
ADGroupSearchBaseDN,
ADGroupSearchFilter,
ADNameAttribute,
ADGroupNameAttribute,
ADUserDNs,
isPageValid,
]);
let inputs = null;
@@ -296,6 +321,60 @@ const IdentityProvider = ({
);
});
}
if (idpSelection === "AD") {
inputs = ADUserDNs.map((_, index) => {
return (
<Fragment key={`identityField-${index.toString()}`}>
<div className={classes.shortened}>
<InputBoxWrapper
id={`ad-userdn-${index.toString()}`}
label={""}
placeholder=""
name={`ad-userdn-${index.toString()}`}
value={ADUserDNs[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateADUserField(index, e.target.value);
cleanValidation(`ad-userdn-${index.toString()}`);
}}
index={index}
key={`csv-ad-userdn-${index.toString()}`}
error={validationErrors[`ad-userdn-${index.toString()}`] || ""}
/>
<div className={classes.buttonTray}>
<Tooltip title="Add User" aria-label="add">
<IconButton
size={"small"}
onClick={() => {
ADUserDNs.push("");
updateADUserField(ADUserDNs.length - 1, "");
}}
>
<AddIcon />
</IconButton>
</Tooltip>
<Tooltip title="Remove" aria-label="add">
<IconButton
size={"small"}
style={{ marginLeft: 16 }}
onClick={() => {
if (ADUserDNs.length > 1) {
ADUserDNs.splice(index, 1);
updateUserField(
ADUserDNs.length - 1,
ADUserDNs[ADUserDNs.length - 1]
);
}
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</div>
</div>
</Fragment>
);
});
}
return (
<Fragment>
<div className={classes.headerElement}>
@@ -320,9 +399,13 @@ const IdentityProvider = ({
{ label: "Active Directory", value: "AD" },
]}
/>
Add additional users
</Grid>{" "}
{idpSelection === "Built-in" && <Fragment>{inputs}</Fragment>}
</Grid>
{idpSelection === "Built-in" && (
<Fragment>
Add additional users
{inputs}
</Fragment>
)}
{idpSelection === "OpenID" && (
<Fragment>
<Grid item xs={12}>
@@ -335,10 +418,26 @@ const IdentityProvider = ({
}}
label="URL"
value={openIDURL}
placeholder="https://your-identity-provider.com/"
error={validationErrors["openID_URL"] || ""}
required
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="openID_CONFIGURATION_URL"
name="openID_CONFIGURATION_URL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDConfigurationURL", e.target.value);
cleanValidation("openID_CONFIGURATION_URL");
}}
label="Configuration URL"
value={openIDConfigurationURL}
placeholder="https://your-identity-provider.com/.well-known/openid-configuration"
error={validationErrors["openID_CONFIGURATION_URL"] || ""}
required
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="openID_clientID"
@@ -367,6 +466,46 @@ const IdentityProvider = ({
required
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="openID_callbackURL"
name="openID_callbackURL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDCallbackURL", e.target.value);
cleanValidation("openID_callbackURL");
}}
label="Callback URL"
value={openIDCallbackURL}
placeholder="https://your-console-endpoint:9443/oauth_callback"
error={validationErrors["openID_callbackURL"] || ""}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="openID_claimName"
name="openID_claimName"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDClaimName", e.target.value);
cleanValidation("openID_claimName");
}}
label="Claim Name"
value={openIDClaimName}
error={validationErrors["openID_claimName"] || ""}
required
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="openID_scopes"
name="openID_scopes"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDScopes", e.target.value);
cleanValidation("openID_scopes");
}}
label="Scopes"
value={openIDScopes}
/>
</Grid>
</Fragment>
)}
{idpSelection === "AD" && (
@@ -379,8 +518,9 @@ const IdentityProvider = ({
updateField("ADURL", e.target.value);
cleanValidation("AD_URL");
}}
label="URL"
label="LDAP Server Address"
value={ADURL}
placeholder="ldap-server:636"
error={validationErrors["AD_URL"] || ""}
required
/>
@@ -394,7 +534,6 @@ const IdentityProvider = ({
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADSkipTLS", checked);
}}
label={"Skip TLS Verification"}
@@ -409,7 +548,6 @@ const IdentityProvider = ({
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADServerInsecure", checked);
}}
label={"Server Insecure"}
@@ -429,31 +567,53 @@ const IdentityProvider = ({
</Grid>
) : null}
<Grid item xs={12}>
<InputBoxWrapper
id="ad_userNameFilter"
name="ad_userNameFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserNameFilter", e.target.value);
cleanValidation("ad_userNameFilter");
<FormSwitchWrapper
value="ad_serverStartTLS"
id="ad_serverStartTLS"
name="ad_serverStartTLS"
checked={ADServerStartTLS}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADServerStartTLS", checked);
}}
label="User Search Filter"
value={ADUserNameFilter}
error={validationErrors["ad_userNameFilter"] || ""}
required
label={"Start TLS connection to AD/LDAP server"}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_groupBaseDN"
name="ad_groupBaseDN"
id="ad_userNameFormat"
name="ad_userNameFormat"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADGroupBaseDN", e.target.value);
cleanValidation("ad_groupBaseDN");
updateField("ADUserNameFormat", e.target.value);
}}
label="Username Format"
value={ADUserNameFormat}
placeholder="uid=%s,cn=accounts,dc=myldapserver,dc=com"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_userNameFilter"
name="ad_userNameFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserNameSearchFilter", e.target.value);
}}
label="Username Search Filter"
value={ADUserNameSearchFilter}
placeholder="(|(objectclass=posixAccount)(uid=%s))"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_groupSearchBaseDN"
name="ad_groupSearchBaseDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADGroupSearchBaseDN", e.target.value);
}}
label="Group Search Base DN"
value={ADGroupBaseDN}
error={validationErrors["ad_groupBaseDN"] || ""}
required
value={ADGroupSearchBaseDN}
placeholder="ou=hwengg,dc=min,dc=io;ou=swengg,dc=min,dc=io"
/>
</Grid>
<Grid item xs={12}>
@@ -462,28 +622,76 @@ const IdentityProvider = ({
name="ad_groupSearchFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADGroupSearchFilter", e.target.value);
cleanValidation("ad_groupSearchFilter");
}}
label="Group Search Filter"
value={ADGroupSearchFilter}
error={validationErrors["ad_groupSearchFilter"] || ""}
required
placeholder="(&(objectclass=groupOfNames)(member=%s))"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_nameAttribute"
name="ad_nameAttribute"
id="ad_groupNameAttribute"
name="ad_groupNameAttribute"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADNameAttribute", e.target.value);
cleanValidation("ad_nameAttribute");
updateField("ADGroupNameAttribute", e.target.value);
}}
label="Group Name Attribute"
value={ADNameAttribute}
error={validationErrors["ad_nameAttribute"] || ""}
required
value={ADGroupNameAttribute}
placeholder="cn"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_lookupBindDN"
name="ad_lookupBindDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADLookupBindDN", e.target.value);
}}
label="Lookup Bind DN"
value={ADLookupBindDN}
placeholder="cn=admin,dc=min,dc=io"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_lookupBindPassword"
name="ad_lookupBindPassword"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADLookupBindPassword", e.target.value);
}}
label="Lookup Bind Password"
value={ADLookupBindPassword}
placeholder="admin"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_userDNSearchBaseDN"
name="ad_userDNSearchBaseDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserDNSearchBaseDN", e.target.value);
}}
label="User DN Search Base DN"
value={ADUserDNSearchBaseDN}
placeholder="dc=min,dc=io"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="ad_userDNSearchFilter"
name="ad_userDNSearchFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserDNSearchFilter", e.target.value);
}}
label="User DN Search Filter"
value={ADUserDNSearchFilter}
placeholder="(uid=%s)"
/>
</Grid>
<Grid item xs={12}>
List of user DNs (Distinguished Names) to be Tenant Administrators
{inputs}
</Grid>
</Fragment>
)}
</Fragment>
@@ -495,22 +703,42 @@ const mapState = (state: AppState) => ({
accessKeys: state.tenants.createTenant.fields.identityProvider.accessKeys,
secretKeys: state.tenants.createTenant.fields.identityProvider.secretKeys,
openIDURL: state.tenants.createTenant.fields.identityProvider.openIDURL,
openIDConfigurationURL:
state.tenants.createTenant.fields.identityProvider.openIDConfigurationURL,
openIDClientID:
state.tenants.createTenant.fields.identityProvider.openIDClientID,
openIDSecretID:
state.tenants.createTenant.fields.identityProvider.openIDSecretID,
openIDCallbackURL:
state.tenants.createTenant.fields.identityProvider.openIDCallbackURL,
openIDClaimName:
state.tenants.createTenant.fields.identityProvider.openIDClaimName,
openIDScopes: state.tenants.createTenant.fields.identityProvider.openIDScopes,
ADURL: state.tenants.createTenant.fields.identityProvider.ADURL,
ADSkipTLS: state.tenants.createTenant.fields.identityProvider.ADSkipTLS,
ADServerInsecure:
state.tenants.createTenant.fields.identityProvider.ADServerInsecure,
ADUserNameFilter:
state.tenants.createTenant.fields.identityProvider.ADUserNameFilter,
ADGroupBaseDN:
state.tenants.createTenant.fields.identityProvider.ADGroupBaseDN,
ADUserNameSearchFilter:
state.tenants.createTenant.fields.identityProvider.ADUserNameSearchFilter,
ADGroupSearchBaseDN:
state.tenants.createTenant.fields.identityProvider.ADGroupSearchBaseDN,
ADGroupSearchFilter:
state.tenants.createTenant.fields.identityProvider.ADGroupSearchFilter,
ADNameAttribute:
state.tenants.createTenant.fields.identityProvider.ADNameAttribute,
ADGroupNameAttribute:
state.tenants.createTenant.fields.identityProvider.ADGroupNameAttribute,
ADUserDNs: state.tenants.createTenant.fields.identityProvider.ADUserDNs,
ADUserNameFormat:
state.tenants.createTenant.fields.identityProvider.ADUserNameFormat,
ADLookupBindDN:
state.tenants.createTenant.fields.identityProvider.ADLookupBindDN,
ADLookupBindPassword:
state.tenants.createTenant.fields.identityProvider.ADLookupBindPassword,
ADUserDNSearchBaseDN:
state.tenants.createTenant.fields.identityProvider.ADUserDNSearchBaseDN,
ADUserDNSearchFilter:
state.tenants.createTenant.fields.identityProvider.ADUserDNSearchFilter,
ADServerStartTLS:
state.tenants.createTenant.fields.identityProvider.ADServerStartTLS,
});
const connector = connect(mapState, {

View File

@@ -90,6 +90,7 @@ const Security = ({
addFileToConsoleCaCertificates,
deleteConsoleCaCertificate,
addConsoleCaCertificate,
addConsoleCertificate,
addKeyPair,
addFileToKeyPair,
deleteKeyPair,
@@ -195,23 +196,6 @@ const Security = ({
</Grid>
{minioCertificates.map((keyPair: KeyPair) => (
<Fragment key={keyPair.id}>
<Grid item xs={5}>
<FileSelector
onChange={(encodedValue, fileName) => {
addFileToKeyPair(
keyPair.id,
"key",
fileName,
encodedValue
);
}}
accept=".key,.pem"
id="tlsKey"
name="tlsKey"
label="Key"
value={keyPair.key}
/>
</Grid>
<Grid item xs={5}>
<FileSelector
onChange={(encodedValue, fileName) => {
@@ -229,6 +213,23 @@ const Security = ({
value={keyPair.cert}
/>
</Grid>
<Grid item xs={5}>
<FileSelector
onChange={(encodedValue, fileName) => {
addFileToKeyPair(
keyPair.id,
"key",
fileName,
encodedValue
);
}}
accept=".key,.pem"
id="tlsKey"
name="tlsKey"
label="Key"
value={keyPair.key}
/>
</Grid>
<Grid item xs={1}>
<Button
onClick={() => {
@@ -310,18 +311,6 @@ const Security = ({
Console Certificates
</Typography>
</Grid>
<Grid item xs={6}>
<FileSelector
onChange={(encodedValue, fileName) => {
addConsoleCertificate("key", fileName, encodedValue);
}}
accept=".key,.pem"
id="consoleKey"
name="consoleKey"
label="Key"
value={consoleCertificate.key}
/>
</Grid>
<Grid item xs={6}>
<FileSelector
onChange={(encodedValue, fileName) => {
@@ -334,6 +323,18 @@ const Security = ({
value={consoleCertificate.cert}
/>
</Grid>
<Grid item xs={6}>
<FileSelector
onChange={(encodedValue, fileName) => {
addConsoleCertificate("key", fileName, encodedValue);
}}
accept=".key,.pem"
id="consoleKey"
name="consoleKey"
label="Key"
value={consoleCertificate.key}
/>
</Grid>
</Grid>
<Grid container>
<Grid item xs={12}>

View File

@@ -98,7 +98,7 @@ export interface ITenant {
consoleTLS: boolean;
consoleEnabled: boolean;
idpAdEnabled: boolean;
idpOicEnabled: boolean;
idpOidcEnabled: boolean;
health_status: string;
status?: ITenantStatus;
// computed

View File

@@ -93,9 +93,9 @@ const mapState = (state: AppState) => ({
false
),
adEnabled: get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false),
oicEnabled: get(
oidcEnabled: get(
state.tenants.tenantDetails.tenantInfo,
"idpOicEnabled",
"idpOidcEnabled",
false
),
});

View File

@@ -503,9 +503,10 @@ const TenantSecurity = ({
display="block"
gutterBottom
>
{certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
{certificateInfo.domains &&
certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
</Typography>
<Typography
className={classes.bold}
@@ -616,9 +617,10 @@ const TenantSecurity = ({
display="block"
gutterBottom
>
{certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
{certificateInfo.domains &&
certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
</Typography>
<Typography
className={classes.bold}
@@ -731,9 +733,10 @@ const TenantSecurity = ({
display="block"
gutterBottom
>
{certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
{certificateInfo.domains &&
certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
</Typography>
<Typography
className={classes.bold}
@@ -829,9 +832,10 @@ const TenantSecurity = ({
display="block"
gutterBottom
>
{certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
{certificateInfo.domains &&
certificateInfo.domains.map((dom) => {
return <div>{dom}</div>;
})}
</Typography>
<Typography
className={classes.bold}

View File

@@ -45,7 +45,7 @@ interface ITenantsSummary {
consoleTLS: boolean;
consoleEnabled: boolean;
adEnabled: boolean;
oicEnabled: boolean;
oidcEnabled: boolean;
loadingTenant: boolean;
}
@@ -86,7 +86,7 @@ const TenantSummary = ({
consoleTLS,
consoleEnabled,
adEnabled,
oicEnabled,
oidcEnabled,
loadingTenant,
}: ITenantsSummary) => {
const [capacity, setCapacity] = useState<number>(0);
@@ -421,36 +421,28 @@ const TenantSummary = ({
<td></td>
</tr>
<tr>
{adEnabled ||
(!adEnabled && !oicEnabled && (
<React.Fragment>
<td className={classes.titleCol}>
Active Directory:
</td>
<td>
<Button
color="primary"
className={classes.anchorButton}
>
{adEnabled ? "Enabled" : "Disabled"}
</Button>
</td>
</React.Fragment>
))}
{oicEnabled ||
(!oicEnabled && !adEnabled && (
<React.Fragment>
<td className={classes.titleCol}>OpenID:</td>
<td>
<Button
color="primary"
className={classes.anchorButton}
>
{oicEnabled ? "Enabled" : "Disabled"}
</Button>
</td>
</React.Fragment>
))}
<React.Fragment>
<td className={classes.titleCol}>Active Directory:</td>
<td>
<Button
color="primary"
className={classes.anchorButton}
>
{adEnabled ? "Enabled" : "Disabled"}
</Button>
</td>
</React.Fragment>
<React.Fragment>
<td className={classes.titleCol}>OpenID:</td>
<td>
<Button
color="primary"
className={classes.anchorButton}
>
{oidcEnabled ? "Enabled" : "Disabled"}
</Button>
</td>
</React.Fragment>
</tr>
</Fragment>
)}
@@ -486,9 +478,9 @@ const mapState = (state: AppState) => ({
false
),
adEnabled: get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false),
oicEnabled: get(
oidcEnabled: get(
state.tenants.tenantDetails.tenantInfo,
"idpOicEnabled",
"idpOidcEnabled",
false
),
});

View File

@@ -88,15 +88,26 @@ const initialState: ITenantState = {
accessKeys: [getRandomString(16)],
secretKeys: [getRandomString(32)],
openIDURL: "",
openIDConfigurationURL: "",
openIDClientID: "",
openIDSecretID: "",
openIDCallbackURL: "",
openIDClaimName: "",
openIDScopes: "",
ADURL: "",
ADSkipTLS: false,
ADServerInsecure: false,
ADUserNameFilter: "",
ADGroupBaseDN: "",
ADUserNameSearchFilter: "",
ADGroupSearchBaseDN: "",
ADGroupSearchFilter: "",
ADNameAttribute: "",
ADGroupNameAttribute: "",
ADUserDNs: [""],
ADUserNameFormat: "",
ADLookupBindDN: "",
ADLookupBindPassword: "",
ADUserDNSearchBaseDN: "",
ADUserDNSearchFilter: "",
ADServerStartTLS: false,
},
security: {
enableAutoCert: true,
@@ -545,15 +556,26 @@ export function tenantsReducer(
accessKeys: [getRandomString(16)],
secretKeys: [getRandomString(32)],
openIDURL: "",
openIDConfigurationURL: "",
openIDClientID: "",
openIDSecretID: "",
openIDCallbackURL: "",
openIDClaimName: "",
openIDScopes: "",
ADURL: "",
ADSkipTLS: false,
ADServerInsecure: false,
ADUserNameFilter: "",
ADGroupBaseDN: "",
ADUserNameSearchFilter: "",
ADGroupSearchBaseDN: "",
ADGroupSearchFilter: "",
ADNameAttribute: "",
ADGroupNameAttribute: "",
ADUserDNs: [""],
ADUserNameFormat: "",
ADLookupBindDN: "",
ADLookupBindPassword: "",
ADUserDNSearchBaseDN: "",
ADUserDNSearchFilter: "",
ADServerStartTLS: false,
},
security: {
enableAutoCert: true,

View File

@@ -150,15 +150,26 @@ export interface IIdentityProviderFields {
accessKeys: string[];
secretKeys: string[];
openIDURL: string;
openIDConfigurationURL: string;
openIDClientID: string;
openIDSecretID: string;
openIDCallbackURL: string;
openIDClaimName: string;
openIDScopes: string;
ADURL: string;
ADSkipTLS: boolean;
ADServerInsecure: boolean;
ADUserNameFilter: string;
ADGroupBaseDN: string;
ADUserNameSearchFilter: string;
ADUserNameFormat: string;
ADGroupSearchBaseDN: string;
ADGroupSearchFilter: string;
ADNameAttribute: string;
ADGroupNameAttribute: string;
ADUserDNs: string[];
ADLookupBindDN: string;
ADLookupBindPassword: string;
ADUserDNSearchBaseDN: string;
ADUserDNSearchFilter: string;
ADServerStartTLS: boolean;
}
export interface ISecurityFields {

View File

@@ -347,10 +347,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
</Typography>
<Button
component={"a"}
href={loginStrategy.redirect.replace(
"%5BHOSTNAME%5D",
window.location.hostname
)}
href={loginStrategy.redirect}
type="submit"
variant="contained"
color="primary"

View File

@@ -3240,8 +3240,6 @@ definitions:
type: object
required:
- url
- username_format
- user_search_filter
properties:
url:
type: string

View File

@@ -2066,7 +2066,7 @@ definitions:
type: boolean
idpAdEnabled:
type: boolean
idpOicEnabled:
idpOidcEnabled:
type: boolean
encryptionEnabled:
type: boolean
@@ -2285,16 +2285,26 @@ definitions:
oidc:
type: object
required:
- configuration_url
- url
- client_id
- secret_id
- claim_name
properties:
url:
type: string
configuration_url:
type: string
client_id:
type: string
secret_id:
type: string
callback_url:
type: string
claim_name:
type: string
scopes:
type: string
keys:
type: array
items:
@@ -2311,14 +2321,12 @@ definitions:
type: object
required:
- url
- username_format
- user_search_filter
properties:
url:
type: string
username_format:
type: string
user_search_filter:
username_search_filter:
type: string
group_search_base_dn:
type: string
@@ -2330,6 +2338,20 @@ definitions:
type: boolean
server_insecure:
type: boolean
server_start_tls:
type: boolean
lookup_bind_dn:
type: string
lookup_bind_password:
type: string
user_dn_search_base_dn:
type: string
user_dn_search_filter:
type: string
user_dns:
type: array
items:
type: string
consoleConfiguration:
allOf: