diff --git a/docs/resources/directory_service_auth_provider.md b/docs/resources/directory_service_auth_provider.md new file mode 100644 index 00000000..cbd0a186 --- /dev/null +++ b/docs/resources/directory_service_auth_provider.md @@ -0,0 +1,287 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "redfish_directory_service_auth_provider Resource - terraform-provider-redfish" +subcategory: "" +description: |- + This Terraform resource is used to configure Directory Service Auth Provider Active Directory and LDAP Service We can Read the existing configurations or modify them using this resource. +--- + +# redfish_directory_service_auth_provider (Resource) + +This Terraform resource is used to configure Directory Service Auth Provider Active Directory and LDAP Service We can Read the existing configurations or modify them using this resource. + +## Example Usage + +```terraform +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +data "local_file" "kerberos" { + # this is the path to the kerberos keytab file that we want to upload. + # this file must be base64 encoded format + filename = "/root/directoryservice/new/terraform-provider-redfish/test-data/kerberos_file.txt" +} + +# redfish_directory_service_auth_provider Terraform resource is used to configure Directory Service Auth Provider Active Directory and LDAP Service +# Available action: Create, Update (Active Directory, LDAP) +# Active Directory (Create, Update): remote_role_mapping, service_addresses, service_enabled,authentication, active_directory_attributes +# LDAP (Create, Update): remote_role_mapping, service_addresses, service_enabled,ldap_service, ldap_attributes +resource "redfish_directory_service_auth_provider" "ds_auth" { + for_each = var.rack1 + + redfish_server { + user = each.value.user + password = each.value.password + endpoint = each.value.endpoint + ssl_insecure = each.value.ssl_insecure + } + + #Note: `active_directory` is mutually inclusive with `active_directory_attributes`. + #Note: `ldap` is mutually inclusive with `ldap_attributes`. + #Note: `active_directory` is mutually exclusive with `ldap`. + #Note: `active_directory_attributes` is mutually exclusive with `ldap_attributes`. + active_directory = { + directory = { + # remote_role_mapping = [ + # { + # local_role = "None", + # remote_group = "idracgroup" + # } + # ], + # service_addresses = [ + # "yulanadhost11.yulan.pie.lab.emc.com" + # ], + service_enabled = true, + authentication = { + kerberos_key_tab_file = data.local_file.kerberos.content + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout" = "120", + "ActiveDirectory.1.CertValidationEnable" = "Enabled", + "ActiveDirectory.1.DCLookupEnable" = "Enabled", + + # RacName and RacDomain can be configured when Schema is Extended Schema + "ActiveDirectory.1.RacDomain" = "test", + "ActiveDirectory.1.RacName" = "test", + + # if SSOEnable is Enabled make sure ActiveDirectory Service is enabled and valid kerberos_key_tab_file is provided + "ActiveDirectory.1.SSOEnable" = "Disabled", + + # Schema can be Extended Schema or Standard Schema + "ActiveDirectory.1.Schema" = "Extended Schema", + "UserDomain.1.Name" = "yulan.pie.lab.emc.com", + + # DCLookupByUserDomain must be configured when DCLookupEnable is enabled + "ActiveDirectory.1.DCLookupByUserDomain" : "Enabled", + + # DCLookupDomainName must be configured when DCLookupByUserDomain is Disabled and DCLookupEnable is Enabled + #"ActiveDirectory.1.DCLookupDomainName"="test", + + #"ActiveDirectory.1.GCLookupEnable" = "Disabled" + + # at least any one from GlobalCatalog1,GlobalCatalog2,GlobalCatalog3 must be configured when Schema is Standard and GCLookupEnable is Disabled + # "ActiveDirectory.1.GlobalCatalog1" = "yulanadhost11.yulan.pie.lab.emc.com", + # "ActiveDirectory.1.GlobalCatalog2" = "yulanadhost11.yulan.pie.lab.emc.com", + # "ActiveDirectory.1.GlobalCatalog3" = "yulanadhost11.yulan.pie.lab.emc.com", + + # GCRootDomain can be configured when GCLookupEnable is Enabled + #"ActiveDirectory.1.GCRootDomain" = "test" + + # RSA Secure configuration required Datacenter license + #"LDAP.1.RSASecurID2FALDAP":"Enabled", + #"RSASecurID2FA.1.RSASecurIDAccessKey": "●●1", + #"RSASecurID2FA.1.RSASecurIDClientID": "●●1", + #"RSASecurID2FA.1.RSASecurIDAuthenticationServer": "", + } + + + + # ldap = { + # directory = { + # remote_role_mapping = [ + # { + # local_role = "Administrator", + # remote_group = "cn = idracgroup,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + # } + # ], + # service_addresses = [ + # "yulanadhost12.yulan.pie.lab.emc.com" + # ], + # service_enabled = false + # }, + # ldap_service = { + # search_settings = { + # base_distinguished_names = [ + # "dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + # ], + # group_name_attribute = "name", + # user_name_attribute = "member" + # } + # } + # } + # + # ldap_attributes = { + # "LDAP.1.GroupAttributeIsDN" = "Enabled" + # "LDAP.1.Port" = "636", + # "LDAP.1.BindDN" = "cn = adtester,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com", + # "LDAP.1.BindPassword" = "", + # "LDAP.1.SearchFilter" = "(objectclass = *)", + # + # #"LDAP.1.RSASecurID2FALDAP":"Enabled", + # #"RSASecurID2FA.1.RSASecurIDAccessKey": "●●1", + # #"RSASecurID2FA.1.RSASecurIDClientID": "●●1", + # #"RSASecurID2FA.1.RSASecurIDAuthenticationServer": "", + # } + +} +``` + + +## Schema + +### Optional + +- `active_directory` (Attributes) Active DirectoryNote: `active_directory` is mutually inclusive with `active_directory_attributes`. , Note: `active_directory` is mutually exclusive with `ldap`. (see [below for nested schema](#nestedatt--active_directory)) +- `active_directory_attributes` (Map of String) ActiveDirectory.* attributes in Dell iDRAC attributes.Note: `active_directory` is mutually inclusive with `active_directory_attributes`. , Note: `active_directory_attributes` is mutually exclusive with `ldap_attributes`. +- `ldap` (Attributes) LDAPNote: `ldap` is mutually inclusive with `ldap_attributes`. , Note: `active_directory` is mutually exclusive with `ldap`. (see [below for nested schema](#nestedatt--ldap)) +- `ldap_attributes` (Map of String) LDAP.* attributes in Dell iDRAC attributes.Note: `ldap` is mutually inclusive with `ldap_attributes`. , Note: `active_directory_attributes` is mutually exclusive with `ldap_attributes`. +- `redfish_server` (Block List) List of server BMCs and their respective user credentials (see [below for nested schema](#nestedblock--redfish_server)) + +### Read-Only + +- `id` (String) ID of the Directory Service Auth Provider resource + + +### Nested Schema for `active_directory` + +Optional: + +- `authentication` (Attributes) Authentication information for the account provider. (see [below for nested schema](#nestedatt--active_directory--authentication)) +- `directory` (Attributes) Directory for Active Directory . (see [below for nested schema](#nestedatt--active_directory--directory)) + + +### Nested Schema for `active_directory.authentication` + +Optional: + +- `kerberos_key_tab_file` (String) KerberosKeytab is a Base64-encoded version of the Kerberos keytab for this Service + + + +### Nested Schema for `active_directory.directory` + +Optional: + +- `remote_role_mapping` (Attributes List) Mapping rules that are used to convert the account providers account information to the local Redfish role (see [below for nested schema](#nestedatt--active_directory--directory--remote_role_mapping)) +- `service_addresses` (List of String) ServiceAddresses of the account providers +- `service_enabled` (Boolean) ServiceEnabled indicate whether this service is enabled. + + +### Nested Schema for `active_directory.directory.remote_role_mapping` + +Optional: + +- `local_role` (String) Role Assigned to the Group. +- `remote_group` (String) Name of the remote group. + + + + + +### Nested Schema for `ldap` + +Optional: + +- `directory` (Attributes) Directory for LDAP. (see [below for nested schema](#nestedatt--ldap--directory)) +- `ldap_service` (Attributes) LDAPService is any additional mapping information needed to parse a generic LDAP service. (see [below for nested schema](#nestedatt--ldap--ldap_service)) + + +### Nested Schema for `ldap.directory` + +Optional: + +- `remote_role_mapping` (Attributes List) Mapping rules that are used to convert the account providers account information to the local Redfish role (see [below for nested schema](#nestedatt--ldap--directory--remote_role_mapping)) +- `service_addresses` (List of String) ServiceAddresses of the account providers +- `service_enabled` (Boolean) ServiceEnabled indicate whether this service is enabled. + + +### Nested Schema for `ldap.directory.remote_role_mapping` + +Optional: + +- `local_role` (String) Role Assigned to the Group. +- `remote_group` (String) Name of the remote group. + + + + +### Nested Schema for `ldap.ldap_service` + +Optional: + +- `search_settings` (Attributes) SearchSettings is the required settings to search an external LDAP service. (see [below for nested schema](#nestedatt--ldap--ldap_service--search_settings)) + + +### Nested Schema for `ldap.ldap_service.search_settings` + +Optional: + +- `base_distinguished_names` (List of String) BaseDistinguishedNames is an array of base distinguished names to use to search an external LDAP service. +- `group_name_attribute` (String) GroupNameAttribute is the attribute name that contains the LDAP group name. +- `user_name_attribute` (String) UsernameAttribute is the attribute name that contains the LDAP user name. + + + + + +### Nested Schema for `redfish_server` + +Optional: + +- `endpoint` (String) Server BMC IP address or hostname +- `password` (String, Sensitive) User password for login +- `redfish_alias` (String) Alias name for server BMCs. The key in provider's `redfish_servers` map +- `ssl_insecure` (Boolean) This field indicates whether the SSL/TLS certificate must be verified or not +- `user` (String) User name for login + +## Import + +Import is supported using the following syntax: + +```shell +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +terraform import redfish_directory_service_auth_provider.ds_auth '{"username":"","password":"","endpoint":"","ssl_insecure":}' +``` diff --git a/examples/resources/redfish_directory_service_auth_provider/import.sh b/examples/resources/redfish_directory_service_auth_provider/import.sh new file mode 100644 index 00000000..9b8033fb --- /dev/null +++ b/examples/resources/redfish_directory_service_auth_provider/import.sh @@ -0,0 +1,18 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +terraform import redfish_directory_service_auth_provider.ds_auth '{"username":"","password":"","endpoint":"","ssl_insecure":}' diff --git a/examples/resources/redfish_directory_service_auth_provider/provider.tf b/examples/resources/redfish_directory_service_auth_provider/provider.tf new file mode 100644 index 00000000..5afaec72 --- /dev/null +++ b/examples/resources/redfish_directory_service_auth_provider/provider.tf @@ -0,0 +1,25 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +terraform { + required_providers { + redfish = { + version = "1.5.0" + source = "registry.terraform.io/dell/redfish" + } + } +} \ No newline at end of file diff --git a/examples/resources/redfish_directory_service_auth_provider/resource.tf b/examples/resources/redfish_directory_service_auth_provider/resource.tf new file mode 100644 index 00000000..d0bb4626 --- /dev/null +++ b/examples/resources/redfish_directory_service_auth_provider/resource.tf @@ -0,0 +1,138 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +data "local_file" "kerberos" { + # this is the path to the kerberos keytab file that we want to upload. + # this file must be base64 encoded format + filename = "/root/directoryservice/new/terraform-provider-redfish/test-data/kerberos_file.txt" +} + +# redfish_directory_service_auth_provider Terraform resource is used to configure Directory Service Auth Provider Active Directory and LDAP Service +# Available action: Create, Update (Active Directory, LDAP) +# Active Directory (Create, Update): remote_role_mapping, service_addresses, service_enabled,authentication, active_directory_attributes +# LDAP (Create, Update): remote_role_mapping, service_addresses, service_enabled,ldap_service, ldap_attributes +resource "redfish_directory_service_auth_provider" "ds_auth" { + for_each = var.rack1 + + redfish_server { + user = each.value.user + password = each.value.password + endpoint = each.value.endpoint + ssl_insecure = each.value.ssl_insecure + } + + #Note: `active_directory` is mutually inclusive with `active_directory_attributes`. + #Note: `ldap` is mutually inclusive with `ldap_attributes`. + #Note: `active_directory` is mutually exclusive with `ldap`. + #Note: `active_directory_attributes` is mutually exclusive with `ldap_attributes`. + active_directory = { + directory = { + # remote_role_mapping = [ + # { + # local_role = "None", + # remote_group = "idracgroup" + # } + # ], + # service_addresses = [ + # "yulanadhost11.yulan.pie.lab.emc.com" + # ], + service_enabled = true, + authentication = { + kerberos_key_tab_file = data.local_file.kerberos.content + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout" = "120", + "ActiveDirectory.1.CertValidationEnable" = "Enabled", + "ActiveDirectory.1.DCLookupEnable" = "Enabled", + + # RacName and RacDomain can be configured when Schema is Extended Schema + "ActiveDirectory.1.RacDomain" = "test", + "ActiveDirectory.1.RacName" = "test", + + # if SSOEnable is Enabled make sure ActiveDirectory Service is enabled and valid kerberos_key_tab_file is provided + "ActiveDirectory.1.SSOEnable" = "Disabled", + + # Schema can be Extended Schema or Standard Schema + "ActiveDirectory.1.Schema" = "Extended Schema", + "UserDomain.1.Name" = "yulan.pie.lab.emc.com", + + # DCLookupByUserDomain must be configured when DCLookupEnable is enabled + "ActiveDirectory.1.DCLookupByUserDomain" : "Enabled", + + # DCLookupDomainName must be configured when DCLookupByUserDomain is Disabled and DCLookupEnable is Enabled + #"ActiveDirectory.1.DCLookupDomainName"="test", + + #"ActiveDirectory.1.GCLookupEnable" = "Disabled" + + # at least any one from GlobalCatalog1,GlobalCatalog2,GlobalCatalog3 must be configured when Schema is Standard and GCLookupEnable is Disabled + # "ActiveDirectory.1.GlobalCatalog1" = "yulanadhost11.yulan.pie.lab.emc.com", + # "ActiveDirectory.1.GlobalCatalog2" = "yulanadhost11.yulan.pie.lab.emc.com", + # "ActiveDirectory.1.GlobalCatalog3" = "yulanadhost11.yulan.pie.lab.emc.com", + + # GCRootDomain can be configured when GCLookupEnable is Enabled + #"ActiveDirectory.1.GCRootDomain" = "test" + + # RSA Secure configuration required Datacenter license + #"LDAP.1.RSASecurID2FALDAP":"Enabled", + #"RSASecurID2FA.1.RSASecurIDAccessKey": "●●1", + #"RSASecurID2FA.1.RSASecurIDClientID": "●●1", + #"RSASecurID2FA.1.RSASecurIDAuthenticationServer": "", + } + + + + # ldap = { + # directory = { + # remote_role_mapping = [ + # { + # local_role = "Administrator", + # remote_group = "cn = idracgroup,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + # } + # ], + # service_addresses = [ + # "yulanadhost12.yulan.pie.lab.emc.com" + # ], + # service_enabled = false + # }, + # ldap_service = { + # search_settings = { + # base_distinguished_names = [ + # "dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + # ], + # group_name_attribute = "name", + # user_name_attribute = "member" + # } + # } + # } + # + # ldap_attributes = { + # "LDAP.1.GroupAttributeIsDN" = "Enabled" + # "LDAP.1.Port" = "636", + # "LDAP.1.BindDN" = "cn = adtester,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com", + # "LDAP.1.BindPassword" = "", + # "LDAP.1.SearchFilter" = "(objectclass = *)", + # + # #"LDAP.1.RSASecurID2FALDAP":"Enabled", + # #"RSASecurID2FA.1.RSASecurIDAccessKey": "●●1", + # #"RSASecurID2FA.1.RSASecurIDClientID": "●●1", + # #"RSASecurID2FA.1.RSASecurIDAuthenticationServer": "", + # } + +} \ No newline at end of file diff --git a/examples/resources/redfish_directory_service_auth_provider/terraform.tfvars b/examples/resources/redfish_directory_service_auth_provider/terraform.tfvars new file mode 100644 index 00000000..f6645b4d --- /dev/null +++ b/examples/resources/redfish_directory_service_auth_provider/terraform.tfvars @@ -0,0 +1,31 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +rack1 = { + "my-server-1" = { + user = "admin" + password = "passw0rd" + endpoint = "https://my-server-1.myawesomecompany.org" + ssl_insecure = true + }, + "my-server-2" = { + user = "admin" + password = "passw0rd" + endpoint = "https://my-server-2.myawesomecompany.org" + ssl_insecure = true + }, +} diff --git a/examples/resources/redfish_directory_service_auth_provider/variables.tf b/examples/resources/redfish_directory_service_auth_provider/variables.tf new file mode 100644 index 00000000..22d751f4 --- /dev/null +++ b/examples/resources/redfish_directory_service_auth_provider/variables.tf @@ -0,0 +1,25 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +variable "rack1" { + type = map(object({ + user = string + password = string + endpoint = string + ssl_insecure = bool + })) +} diff --git a/redfish/models/directory_service_auth_provider.go b/redfish/models/directory_service_auth_provider.go index 49785060..68575df6 100644 --- a/redfish/models/directory_service_auth_provider.go +++ b/redfish/models/directory_service_auth_provider.go @@ -21,6 +21,53 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +// DirectoryServiceAuthProviderResource is the tfsdk model of DirectoryServiceAuthProviderResource +type DirectoryServiceAuthProviderResource struct { + RedfishServer []RedfishServer `tfsdk:"redfish_server"` + ID types.String `tfsdk:"id"` + // Optional Param + ActiveDirectoryResource types.Object `tfsdk:"active_directory"` + LDAPResource types.Object `tfsdk:"ldap"` + ActiveDirectoryAttributes types.Map `tfsdk:"active_directory_attributes"` + LDAPAttributes types.Map `tfsdk:"ldap_attributes"` +} + +// DirectoryResource is the tfsdk model of DirectoryResource +type DirectoryResource struct { + RemoteRoleMapping types.List `tfsdk:"remote_role_mapping"` + ServiceAddresses []types.String `tfsdk:"service_addresses"` + ServiceEnabled types.Bool `tfsdk:"service_enabled"` +} + +// ActiveDirectoryResource is the tfsdk model of ActiveDirectoryResource +type ActiveDirectoryResource struct { + Directory types.Object `tfsdk:"directory"` + Authentication types.Object `tfsdk:"authentication"` +} + +// AuthenticationResource is the tfsdk model of AuthenticationResource +type AuthenticationResource struct { + KerberosKeytab types.String `tfsdk:"kerberos_key_tab_file"` +} + +// LDAPResource is the tfsdk model of LDAPResource +type LDAPResource struct { + Directory types.Object `tfsdk:"directory"` + LDAPService types.Object `tfsdk:"ldap_service"` +} + +// LDAPServiceResource is the tfsdk model of LDAPServiceResource +type LDAPServiceResource struct { + SearchSettings types.Object `tfsdk:"search_settings"` +} + +// SearchSettingsResource is the tfsdk model of SearchSettingsResource +type SearchSettingsResource struct { + BaseDistinguishedNames []types.String `tfsdk:"base_distinguished_names"` + UsernameAttribute types.String `tfsdk:"user_name_attribute"` + GroupNameAttribute types.String `tfsdk:"group_name_attribute"` +} + // DirectoryServiceAuthProviderDatasource to construct terraform schema for the auth provider resource. type DirectoryServiceAuthProviderDatasource struct { ID types.String `tfsdk:"id"` diff --git a/redfish/provider/provider.go b/redfish/provider/provider.go index 85e0a3f2..edfc748a 100644 --- a/redfish/provider/provider.go +++ b/redfish/provider/provider.go @@ -171,6 +171,7 @@ func (*redfishProvider) Resources(_ context.Context) []func() resource.Resource NewScpImportResource, NewScpExportResource, NewRedfishNICResource, + NewRedfishDirectoryServiceAuthProviderResource, } } diff --git a/redfish/provider/resource_redfish_directory_service_auth_provider.go b/redfish/provider/resource_redfish_directory_service_auth_provider.go new file mode 100644 index 00000000..925393b6 --- /dev/null +++ b/redfish/provider/resource_redfish_directory_service_auth_provider.go @@ -0,0 +1,446 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "encoding/json" + "io" + "terraform-provider-redfish/redfish/models" + + tfpath "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stmcginnis/gofish" + "github.com/stmcginnis/gofish/redfish" +) + +const ( + noteMessageUpdateOneServiceOnly = "Please update one of active_directory or ldap at a time." + noteADMessageInclusive = "Note: `active_directory` is mutually inclusive with `active_directory_attributes`." + noteLDAPMessageInclusive = "Note: `ldap` is mutually inclusive with `ldap_attributes`." + noteMessageExclusive = "Note: `active_directory` is mutually exclusive with `ldap`." + noteAttributesMessageExclusive = "Note: `active_directory_attributes` is mutually exclusive with `ldap_attributes`." +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &RedfishDirectoryServiceAuthProviderResource{} +) + +// NewRedfishDirectoryServiceAuthProviderResource is a helper function to simplify the provider implementation. +func NewRedfishDirectoryServiceAuthProviderResource() resource.Resource { + return &RedfishDirectoryServiceAuthProviderResource{} +} + +// RedfishDirectoryServiceAuthProviderResource is the resource implementation. +type RedfishDirectoryServiceAuthProviderResource struct { + p *redfishProvider + ctx context.Context +} + +// Configure implements resource.ResourceWithConfigure +func (r *RedfishDirectoryServiceAuthProviderResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.p = req.ProviderData.(*redfishProvider) +} + +// Metadata returns the resource type name. +func (*RedfishDirectoryServiceAuthProviderResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "directory_service_auth_provider" +} + +// Schema defines the schema for the resource. +func (*RedfishDirectoryServiceAuthProviderResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "This Terraform resource is used to configure Directory Service Auth Provider Active Directory and LDAP Service" + + " We can Read the existing configurations or modify them using this resource.", + Description: "This Terraform resource is used to configure Directory Service Auth Provider Active Directory and LDAP Service" + + " We can Read the existing configurations or modify them using this resource.", + + Attributes: DirectoryServiceAuthProviderResourceSchema(), + Blocks: RedfishServerResourceBlockMap(), + } +} + +// Create creates the resource and sets the initial Terraform state. +func (r *RedfishDirectoryServiceAuthProviderResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + r.ctx = ctx + tflog.Trace(ctx, "resource_directory_service_auth_provider create : Started") + // Get Plan Data + var plan, emptyState models.DirectoryServiceAuthProviderResource + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + api, err := NewConfig(r.p, &plan.RedfishServer) + if err != nil { + resp.Diagnostics.AddError("service error", err.Error()) + return + } + service := api.Service + defer api.Logout() + + activeServiceChanged := newActiveDirectoryChanged(ctx, &plan, &emptyState) + ldapServiceChanged := newLDAPChanged(ctx, &plan, &emptyState) + + if activeServiceChanged && ldapServiceChanged { + resp.Diagnostics.AddError("Error when creating both of `ActiveDirectory` and `LDAP`", + noteMessageUpdateOneServiceOnly) + return + } + diags = r.updateRedfishDirectoryServiceAuth(ctx, service, &plan, &emptyState) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + tflog.Trace(ctx, "resource_directory_service_auth_provider create: updating state finished, saving ...") + diags = r.readRedfishDirectoryServiceAuthProvider(ctx, service, &plan) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + tflog.Trace(ctx, "resource_directory_service_auth_provider create: finished state update") + + // Save into State + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + tflog.Trace(ctx, "resource_directory_service_auth_provider create: finish") +} + +// Read refreshes the Terraform state with the latest data. +func (r *RedfishDirectoryServiceAuthProviderResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + tflog.Trace(ctx, "resource_directory_service_auth_provider read: started") + r.ctx = ctx + var state models.DirectoryServiceAuthProviderResource + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + api, err := NewConfig(r.p, &state.RedfishServer) + if err != nil { + resp.Diagnostics.AddError("service error", err.Error()) + return + } + service := api.Service + defer api.Logout() + + diags = r.readRedfishDirectoryServiceAuthProvider(ctx, service, &state) + if diags.HasError() { + diags.AddError("Error running job", "error in reading the directory service") + } + + var idracAttribute models.DellIdracAttributes + diags = readRedfishDellIdracAttributes(ctx, service, &idracAttribute) + resp.Diagnostics.Append(diags...) + + tflog.Trace(ctx, "resource_directory_service_auth_provider read: finished reading state") + // Save into State + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + tflog.Trace(ctx, "resource_directory_service_auth_provider read: finished") +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *RedfishDirectoryServiceAuthProviderResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + r.ctx = ctx + var state, plan models.DirectoryServiceAuthProviderResource + // Get state Data + tflog.Trace(ctx, "resource_directory_service_auth_provider update: started") + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Get plan Data + diags = req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + api, err := NewConfig(r.p, &plan.RedfishServer) + if err != nil { + resp.Diagnostics.AddError("service error", err.Error()) + return + } + service := api.Service + defer api.Logout() + + activeServiceChanged := newActiveDirectoryChanged(ctx, &plan, &state) + ldapServiceChanged := newLDAPChanged(ctx, &plan, &state) + if activeServiceChanged && ldapServiceChanged { + resp.Diagnostics.AddError("Error when updating both of `ActiveDirectory` and `LDAP`", + "Please update one of active_directory or ldap at a time.") + return + } + diags = r.updateRedfishDirectoryServiceAuth(ctx, service, &plan, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Trace(ctx, "resource_directory_service_auth_provider update: finished state update") + // Save into State + diags = r.readRedfishDirectoryServiceAuthProvider(ctx, service, &plan) + if diags.HasError() { + diags.AddError("Error running job", "error in reading the directory service") + } + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + tflog.Trace(ctx, "resource_directory_service_auth_provider update: finished") +} + +// Delete deletes the resource and removes the Terraform state on success. +func (*RedfishDirectoryServiceAuthProviderResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + tflog.Trace(ctx, "resource_directory_service_auth_provider delete: started") + // Get State Data + var state models.DirectoryServiceAuthProviderResource + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.State.RemoveResource(ctx) + tflog.Trace(ctx, "resource_directory_service_auth_provider delete: finished") +} + +// ImportState import state for existing resource +// nolint:revive +func (*RedfishDirectoryServiceAuthProviderResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + type creds struct { + Username string `json:"username"` + Password string `json:"password"` + Endpoint string `json:"endpoint"` + SslInsecure bool `json:"ssl_insecure"` + RedfishAlias string `json:"redfish_alias"` + } + + var c creds + err := json.Unmarshal([]byte(req.ID), &c) + if err != nil { + resp.Diagnostics.AddError("Error while unmarshalling id", err.Error()) + } + + server := models.RedfishServer{ + User: types.StringValue(c.Username), + Password: types.StringValue(c.Password), + Endpoint: types.StringValue(c.Endpoint), + SslInsecure: types.BoolValue(c.SslInsecure), + RedfishAlias: types.StringValue(c.RedfishAlias), + } + + redfishServer := tfpath.Root("redfish_server") + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, tfpath.Root("id"), "importId")...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, redfishServer, []models.RedfishServer{server})...) +} + +// nolint: gofumpt +func (*RedfishDirectoryServiceAuthProviderResource) updateRedfishDirectoryServiceAuth(ctx context.Context, service *gofish.Service, plan, + state *models.DirectoryServiceAuthProviderResource) diag.Diagnostics { + var diags diag.Diagnostics + // Lock the mutex to avoid race conditions with other resources + redfishMutexKV.Lock(plan.RedfishServer[0].Endpoint.ValueString()) + defer redfishMutexKV.Unlock(plan.RedfishServer[0].Endpoint.ValueString()) + + activeServiceChanged := newActiveDirectoryChanged(ctx, plan, state) + ldapServiceChanged := newLDAPChanged(ctx, plan, state) + + // get the account service resource and ODATA_ID will be used to make a patch call + accountService, err := service.AccountService() + if err != nil { + diags.AddError("error fetching accountservice resource", err.Error()) + return diags + } + + accountServiceURI := accountService.ODataID + + if activeServiceChanged { + if diags = updateActiveDirectory(ctx, accountServiceURI, service, plan); diags.HasError() { + return diags + } + } else if ldapServiceChanged { + if diags = updateLDAP(ctx, accountServiceURI, service, plan); diags.HasError() { + return diags + } + } + return diags +} + +func getAccountServiceDetails(service *gofish.Service) (*redfish.AccountService, error) { + accountService, err := service.AccountService() + if err != nil { + return nil, err + } + + return accountService, nil +} + +// nolint: revive +func (*RedfishDirectoryServiceAuthProviderResource) readRedfishDirectoryServiceAuthProvider(ctx context.Context, service *gofish.Service, state *models.DirectoryServiceAuthProviderResource) (diags diag.Diagnostics) { + // var diags diag.Diagnostics + accountService, err := getAccountServiceDetails(service) + if err != nil { + diags.AddError("Error fetching Account Service", err.Error()) + return diags + } + + if diags = parseActiveDirectoryIntoState(ctx, accountService, service, state); diags.HasError() { + diags.AddError("ActiveDir state null", "ActiveDirectory state null") + return diags + } + if diags = parseLDAPIntoState(ctx, accountService, service, state); diags.HasError() { + diags.AddError("oldLDAPState state null", "oldLDAPState state null") + return diags + } + state.ID = types.StringValue("redfish_directory_service_auth_provider") + return diags +} + +// nolint: revive +func updateActiveDirectory(ctx context.Context, serviceURI string, service *gofish.Service, plan *models.DirectoryServiceAuthProviderResource) (diags diag.Diagnostics) { + // var diags diag.Diagnostic + + // Check for all valid scenario + if authTimeOutCheck, diags := isValidAuthTime(ActiveDirectory, ".AuthTimeout", plan); diags.HasError() || !authTimeOutCheck { + return diags + } + + if ssoCheck, diags := isSSOEnabledWithValidFile(ctx, ActiveDirectory, "SSOEnable", plan); diags.HasError() || !ssoCheck { + return diags + } + + dcLookupDomainCheck, diags := isValidDCLookupDomainConfig(ctx, ActiveDirectory, "DCLookupEnable", plan) + if diags.HasError() || !dcLookupDomainCheck { + return diags + } + if schemacheck, diags := isValidSchemaSelection(ctx, ActiveDirectory, "Schema", plan); diags.HasError() || !schemacheck { + return diags + } + + if authFactorCheck, diags := isValid2FactorAuth(plan.ActiveDirectoryAttributes); diags.HasError() || !authFactorCheck { + return diags + } + + patchBody := make(map[string]interface{}) + if patchBody[ActiveDirectory], diags = getActiveDirectoryPatchBody(ctx, plan); diags.HasError() { + return diags + } + + // make a patch call to update the account service activeDirectory configuration + response, err := service.GetClient().Patch(serviceURI, patchBody) + if err != nil { + diags.AddError("There was an error while creating/ updating Active Directory", + "There was an error while creating/ updating Active Directory "+err.Error()) + return diags + } + if response != nil { + body, err := io.ReadAll(response.Body) + if err != nil { + diags.AddError("error reading response body", "error "+string(body)) + return diags + } + readResponse := make(map[string]json.RawMessage) + err = json.Unmarshal(body, &readResponse) + if err != nil { + diags.AddError("Error unmarshalling response body", err.Error()) + return diags + } + + // check for extended error message in response + errorMsg, ok := readResponse["error"] + if ok { + diags.AddError("Error updating AccountService Details", string(errorMsg)) + return diags + } + } + + defer response.Body.Close() + var idracAttributesPlan models.DellIdracAttributes + idracAttributesPlan.Attributes = plan.ActiveDirectoryAttributes + idracAttributesPlan.ID = plan.ID + idracAttributesPlan.RedfishServer = plan.RedfishServer + diags = updateRedfishDellIdracAttributes(ctx, service, &idracAttributesPlan) + if diags.HasError() { + return diags + } + plan.ActiveDirectoryAttributes = idracAttributesPlan.Attributes + return diags +} + +// nolint: revive +func updateLDAP(ctx context.Context, serviceURI string, service *gofish.Service, plan *models.DirectoryServiceAuthProviderResource) (diags diag.Diagnostics) { + if authFactorCheck, diags := isValid2FactorAuth(plan.LDAPAttributes); diags.HasError() || !authFactorCheck { + return diags + } + + patchBody := make(map[string]interface{}) + if patchBody["LDAP"], diags = getLDAPPatchBody(ctx, plan); diags.HasError() { + return diags + } + + // make a patch call to update the account service activeDirectory configuration + response, err := service.GetClient().Patch(serviceURI, patchBody) + if err != nil { + diags.AddError("There was an error while creating/ updating LDAP", "There was an error while creating/ updating LDAP") + return diags + } + + if response != nil { + body, err := io.ReadAll(response.Body) + if err != nil { + return diags + } + readResponse := make(map[string]json.RawMessage) + err = json.Unmarshal(body, &readResponse) + if err != nil { + diags.AddError("Error unmarshalling response body", err.Error()) + return diags + } + // check for extended error message in response + errorMsg, ok := readResponse["error"] + if ok { + diags.AddError("Error updating AccountService Details", string(errorMsg)) + return diags + } + } + defer response.Body.Close() + var idracAttributesPlan models.DellIdracAttributes + idracAttributesPlan.Attributes = plan.LDAPAttributes + idracAttributesPlan.ID = plan.ID + idracAttributesPlan.RedfishServer = plan.RedfishServer + diags = updateRedfishDellIdracAttributes(ctx, service, &idracAttributesPlan) + // resp.Diagnostics.Append(diags...) + if diags.HasError() { + diags.AddError("Idrac update fail", "Idrac update fail due to invalid LDAP configuration") + return diags + } + + plan.LDAPAttributes = idracAttributesPlan.Attributes + return diags +} diff --git a/redfish/provider/resource_redfish_directory_service_auth_provider_helper.go b/redfish/provider/resource_redfish_directory_service_auth_provider_helper.go new file mode 100644 index 00000000..6d28e859 --- /dev/null +++ b/redfish/provider/resource_redfish_directory_service_auth_provider_helper.go @@ -0,0 +1,545 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "strconv" + "strings" + "terraform-provider-redfish/redfish/models" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +const ( + // RSASecurID2FA is rsa Secure Id 2 factor authentication + RSASecurID2FA = "RSASecurID2FA" + // Disabled disable the service + Disabled = "Disabled" + // Enabled enable the service + Enabled = "Enabled" + + lowerLimit = 15 + upperLimit = 300 +) + +// nolint: gocyclo,revive +func newActiveDirectoryChanged(ctx context.Context, plan, state *models.DirectoryServiceAuthProviderResource) bool { + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + + if plan.ActiveDirectoryResource.IsNull() || plan.ActiveDirectoryResource.IsUnknown() { + return false + } + var activeDirectoryPlan models.ActiveDirectoryResource + if !plan.ActiveDirectoryResource.IsNull() && !plan.ActiveDirectoryResource.IsUnknown() { + if diags := plan.ActiveDirectoryResource.As(ctx, &activeDirectoryPlan, objectAsOptions); diags.HasError() { + return false + } + } + + var activeDirectoryState models.ActiveDirectoryResource + if !state.ActiveDirectoryResource.IsNull() && !state.ActiveDirectoryResource.IsUnknown() { + if diags := state.ActiveDirectoryResource.As(ctx, &activeDirectoryState, objectAsOptions); diags.HasError() { + return false + } + } + + var directoryPlan models.DirectoryResource + if !activeDirectoryPlan.Directory.IsNull() && !activeDirectoryPlan.Directory.IsUnknown() { + if diags := activeDirectoryPlan.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return false + } + } + + var directoryState models.DirectoryResource + if !activeDirectoryState.Directory.IsNull() && !activeDirectoryState.Directory.IsUnknown() { + if diags := activeDirectoryState.Directory.As(ctx, &directoryState, objectAsOptions); diags.HasError() { + return false + } + } + + if directoryPlan.RemoteRoleMapping.String() != "" && directoryPlan.RemoteRoleMapping.String() != directoryState.RemoteRoleMapping.String() { + return true + } + + if len(directoryPlan.ServiceAddresses) != len(directoryState.ServiceAddresses) { + return true + } + + if checkListMatching(directoryPlan.ServiceAddresses, directoryState.ServiceAddresses) { + return true + } + + var authenticationPlan models.AuthenticationResource + if !activeDirectoryPlan.Authentication.IsNull() && !activeDirectoryPlan.Authentication.IsUnknown() { + if diags := activeDirectoryPlan.Authentication.As(ctx, &authenticationPlan, objectAsOptions); diags.HasError() { + return false + } + if !authenticationPlan.KerberosKeytab.IsNull() && !authenticationPlan.KerberosKeytab.IsUnknown() { + return true + } + } + + return newAttributesChanged(plan.ActiveDirectoryAttributes, state.ActiveDirectoryAttributes) +} + +// nolint: gocyclo,revive +func newLDAPChanged(ctx context.Context, plan, state *models.DirectoryServiceAuthProviderResource) bool { + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + + if plan.LDAPResource.IsNull() || plan.LDAPResource.IsUnknown() { + return false + } + var ldapPlan models.LDAPResource + if !plan.LDAPResource.IsNull() && !plan.LDAPResource.IsUnknown() { + if diags := plan.LDAPResource.As(ctx, &ldapPlan, objectAsOptions); diags.HasError() { + return false + } + } + var ldapState models.LDAPResource + if !state.LDAPResource.IsNull() && !state.LDAPResource.IsUnknown() { + if diags := state.LDAPResource.As(ctx, &ldapState, objectAsOptions); diags.HasError() { + return false + } + } + var directoryPlan models.DirectoryResource + if !ldapPlan.Directory.IsNull() && !ldapPlan.Directory.IsUnknown() { + if diags := ldapPlan.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return false + } + } + + var directoryState models.DirectoryResource + if !ldapState.Directory.IsNull() && !ldapState.Directory.IsUnknown() { + if diags := ldapState.Directory.As(ctx, &directoryState, objectAsOptions); diags.HasError() { + return false + } + } + + if directoryPlan.RemoteRoleMapping.String() != "" && directoryPlan.RemoteRoleMapping.String() != directoryState.RemoteRoleMapping.String() { + return true + } + + if len(directoryPlan.ServiceAddresses) != len(directoryState.ServiceAddresses) { + return true + } + + if checkListMatching(directoryPlan.ServiceAddresses, directoryState.ServiceAddresses) { + return true + } + + var ldapServicePlan models.LDAPServiceResource + if !ldapPlan.LDAPService.IsNull() && !ldapPlan.LDAPService.IsUnknown() { + if diags := ldapPlan.LDAPService.As(ctx, &ldapServicePlan, objectAsOptions); diags.HasError() { + return false + } + } + + var ldapServiceState models.LDAPServiceResource + if !ldapState.LDAPService.IsNull() && !ldapState.LDAPService.IsUnknown() { + if diags := ldapState.LDAPService.As(ctx, &ldapServiceState, objectAsOptions); diags.HasError() { + return false + } + } + + var ldapSearchSettingsPlan models.SearchSettingsResource + if !ldapServicePlan.SearchSettings.IsNull() && !ldapServicePlan.SearchSettings.IsUnknown() { + if diags := ldapServicePlan.SearchSettings.As(ctx, &ldapSearchSettingsPlan, objectAsOptions); diags.HasError() { + return false + } + } + + var ldapSearchSettingsState models.SearchSettingsResource + if !ldapServiceState.SearchSettings.IsNull() && !ldapServiceState.SearchSettings.IsUnknown() { + if diags := ldapServiceState.SearchSettings.As(ctx, &ldapSearchSettingsState, objectAsOptions); diags.HasError() { + return false + } + } + + if ldapSearchSettingsPlan.GroupNameAttribute.ValueString() != "" && + ldapSearchSettingsPlan.GroupNameAttribute.ValueString() != ldapSearchSettingsState.GroupNameAttribute.ValueString() { + return true + } + if ldapSearchSettingsPlan.UsernameAttribute.ValueString() != "" && + ldapSearchSettingsPlan.UsernameAttribute.ValueString() != ldapSearchSettingsState.UsernameAttribute.ValueString() { + return true + } + + if len(ldapSearchSettingsPlan.BaseDistinguishedNames) != len(ldapSearchSettingsState.BaseDistinguishedNames) { + return true + } + + if checkListMatching(ldapSearchSettingsPlan.BaseDistinguishedNames, ldapSearchSettingsState.BaseDistinguishedNames) { + return true + } + + return newAttributesChanged(plan.LDAPAttributes, state.LDAPAttributes) +} + +func newAttributesChanged(attrsPlan, attrsState types.Map) bool { + if attrsPlan.IsNull() || attrsPlan.IsUnknown() { + return false + } + + if attrsPlan.String() != "" && attrsPlan.String() == attrsState.String() { + return false + } + return true +} + +func checkListMatching(planList []types.String, stateList []types.String) bool { + changed := false + for _, planTarget := range planList { + changed = true + for _, stateTarget := range stateList { + if planTarget.String() == stateTarget.String() { + changed = false + break + } + } + } + return changed +} + +func checkAttributeskeyPresent(attributes types.Map, prefix string, suffix string) bool { + for k := range attributes.Elements() { + if strings.HasPrefix(k, prefix) && strings.HasSuffix(k, suffix) { + return true + } + } + return false +} + +func getkAttributeskeyValue(attributes types.Map, prefix string, suffix string) string { + for k, v := range attributes.Elements() { + if strings.HasPrefix(k, prefix) && strings.HasSuffix(k, suffix) { + value := v.String() + value = value[1 : len(value)-1] + return value + } + } + return "" +} + +func isValid2FactorAuth(attributes types.Map) (bool, diag.Diagnostics) { + var diags diag.Diagnostics + // attributes := attrsState.ActiveDirectoryAttributes + checkey2FA := checkAttributeskeyPresent(attributes, RSASecurID2FA, "RSASecurIDAccessKey") + checkID2FA := checkAttributeskeyPresent(attributes, RSASecurID2FA, "RSASecurIDClientID") + checkServer2FA := checkAttributeskeyPresent(attributes, RSASecurID2FA, "RSASecurIDAuthenticationServer") + + if checkey2FA || checkID2FA || checkServer2FA { + checkey2FAValue := getkAttributeskeyValue(attributes, RSASecurID2FA, "RSASecurIDAccessKey") + checID2FAValue := getkAttributeskeyValue(attributes, RSASecurID2FA, "RSASecurIDClientID") + checkServer2FAValue := getkAttributeskeyValue(attributes, RSASecurID2FA, "RSASecurIDAuthenticationServer") + + if checkey2FAValue == "" || checID2FAValue == "" || checkServer2FAValue == "" { + diags.AddError("Missing RSASecurID2FA required params", "Please provide all the required configuration for 2 factor autentication") + return false, diags + } + } + + return true, diags +} + +func isValidAuthTime(prefix string, suffix string, attrsState *models.DirectoryServiceAuthProviderResource) (bool, diag.Diagnostics) { + var diags diag.Diagnostics + attributes := attrsState.ActiveDirectoryAttributes + check := checkAttributeskeyPresent(attributes, prefix, suffix) + + if !check { + diags.AddError("Invalid AuthTimeout, Please provide all the required configuration", "Please provide all the required configuration") + return false, diags + } + value := getkAttributeskeyValue(attributes, prefix, suffix) + + intValue, err := strconv.Atoi(value) + if err != nil { + diags.AddError("Invalid AuthTimeout", "Invalid AuthTimeout") + return false, diags + } + + if intValue < lowerLimit || intValue > upperLimit { + diags.AddError("Invalid AuthTimeout, AuthTimeout must be between 15 and 300", "AuthTimeout must be between 15 and 300") + return false, diags + } + + return true, nil +} + +// nolint: revive +func isSSOEnabledWithValidFile(ctx context.Context, prefix string, suffix string, attrsState *models.DirectoryServiceAuthProviderResource) (bool, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := attrsState.ActiveDirectoryAttributes + check := checkAttributeskeyPresent(attributes, prefix, suffix) + + if check { + value := getkAttributeskeyValue(attributes, prefix, suffix) + if value == Enabled { + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + var activeDirectoryPlan models.ActiveDirectoryResource + if !attrsState.ActiveDirectoryResource.IsNull() && !attrsState.ActiveDirectoryResource.IsUnknown() { + if diags := attrsState.ActiveDirectoryResource.As(ctx, &activeDirectoryPlan, objectAsOptions); diags.HasError() { + return false, diags + } + } + + var directoryPlan models.DirectoryResource + if !activeDirectoryPlan.Directory.IsNull() && !activeDirectoryPlan.Directory.IsUnknown() { + if diags := activeDirectoryPlan.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return false, diags + } + } + + if !directoryPlan.ServiceEnabled.ValueBool() { + diags.AddError("Please provide valid Configuration for SSO", "SSO can't be enabled when Active Directory service is Disabled") + return false, diags + } + var authenticationPlan models.AuthenticationResource + if !activeDirectoryPlan.Authentication.IsNull() && !activeDirectoryPlan.Authentication.IsUnknown() { + if diags := activeDirectoryPlan.Authentication.As(ctx, &authenticationPlan, objectAsOptions); diags.HasError() { + return false, diags + } + } + + if activeDirectoryPlan.Authentication.IsNull() || activeDirectoryPlan.Authentication.IsUnknown() || + authenticationPlan.KerberosKeytab.IsNull() || authenticationPlan.KerberosKeytab.IsUnknown() { + diags.AddError("Please provide valid kerberos key tab file when SSO is enabled", + "Please provide valid kerberos key tab file when SSO is enabled") + return false, diags + } + + return true, diags + } + + if value == Disabled { + diags.AddWarning("Disabled ", "inside Disabled") + return true, diags + } + } + + return true, diags +} + +// nolint: gocyclo, revive +func isValidSchemaSelection(ctx context.Context, prefix string, suffix string, attrsState *models.DirectoryServiceAuthProviderResource) (bool, diag.Diagnostics) { + var diags diag.Diagnostics + attributes := attrsState.ActiveDirectoryAttributes + check := checkAttributeskeyPresent(attributes, prefix, suffix) + if check { + value := getkAttributeskeyValue(attributes, prefix, suffix) + iDRAcName := checkAttributeskeyPresent(attributes, ActiveDirectory, "RacName") + iDRAcDomain := checkAttributeskeyPresent(attributes, ActiveDirectory, "RacDomain") + groupDomain := checkAttributeskeyPresent(attributes, "ADGroup", "Domain") + gcLookupEnable := checkAttributeskeyPresent(attributes, ActiveDirectory, "GCLookupEnable") + gc1 := checkAttributeskeyPresent(attributes, ActiveDirectory, "GlobalCatalog1") + gc2 := checkAttributeskeyPresent(attributes, ActiveDirectory, "GlobalCatalog2") + gc3 := checkAttributeskeyPresent(attributes, ActiveDirectory, "GlobalCatalog3") + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + var activeDirectoryPlan models.ActiveDirectoryResource + if !attrsState.ActiveDirectoryResource.IsNull() && !attrsState.ActiveDirectoryResource.IsUnknown() { + if diags := attrsState.ActiveDirectoryResource.As(ctx, &activeDirectoryPlan, objectAsOptions); diags.HasError() { + return false, diags + } + } + + var directoryPlan models.DirectoryResource + if !activeDirectoryPlan.Directory.IsNull() && !activeDirectoryPlan.Directory.IsUnknown() { + if diags := activeDirectoryPlan.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return false, diags + } + } + + var remoteRoleMapping []models.RemoteRoleMapping + if diags := directoryPlan.RemoteRoleMapping.ElementsAs(ctx, &remoteRoleMapping, true); diags.HasError() { + return false, diags + } + + if value == "Extended Schema" { + if !iDRAcName || !iDRAcDomain { + diags.AddError("Please provide valid config for RacName and RacDomain", + "RacName and RacDomain must be configured for Extended Schema") + return false, diags + } + iDRAcNameValue := getkAttributeskeyValue(attributes, ActiveDirectory, "RacName") + iDRAcDomainValue := getkAttributeskeyValue(attributes, ActiveDirectory, "RacDomain") + if iDRAcNameValue == "" || iDRAcDomainValue == "" { + diags.AddError("RacName and RacDomain can not be null for Extended Schema", + "Please provide valid configuration for RacName and RacDomain") + return false, diags + } + if gcLookupEnable || gc1 || gc2 || gc3 { + diags.AddError("GCLookupEnable, GlobalCatalog1, GlobalCatalog2, GlobalCatalog3 can not be configured for Extended Schema", + "GCLookupEnable, GlobalCatalog1, GlobalCatalog2, GlobalCatalog3 can not be configured for Extended Schema") + return false, diags + } + + if !activeDirectoryPlan.Directory.IsNull() && !activeDirectoryPlan.Directory.IsUnknown() { + if len(remoteRoleMapping) != 0 { + diags.AddError("RemoteRoleMapping can not be configured for Extended Schema", + "RemoteRoleMapping can not be configured for Extended Schema") + return false, diags + + } + } + + if groupDomain { + diags.AddError("Domain can not be configured for Extended Schema", "Domain can not be configured for Extended Schema") + return false, diags + } + } + if value == "Standard Schema" { + if iDRAcName || iDRAcDomain { + diags.AddError("RacName and RacDomain can not be configured for Standard Schema", + "RacName and RacDomain can not be configured for Standard Schema") + return false, diags + } + if !gcLookupEnable { + diags.AddError("GCLookupEnable must be configured for Standard Schema", "GCLookupEnable must be configured for Standard Schema") + return false, diags + } + gcLookupEnableValue := getkAttributeskeyValue(attributes, ActiveDirectory, "GCLookupEnable") + + if gcLookupEnableValue == Enabled { + gcRootDomain := checkAttributeskeyPresent(attributes, ActiveDirectory, "GCRootDomain") + if !gcRootDomain || getkAttributeskeyValue(attributes, ActiveDirectory, "GCRootDomain") == "" { + diags.AddError("GCRootDomain must be configured for Enabled GCLookupEnable", + "GCRootDomain must be configured for Enabled GCLookupEnable") + return false, diags + } + + if gc1 || gc2 || gc3 { + diags.AddError("GlobalCatalog can not be configured for Enabled GCLookupEnable", + " GlobalCatalog can not be configured for Enabled GCLookupEnable") + return false, diags + } + } else if gcLookupEnableValue == Disabled { + gc1Value := getkAttributeskeyValue(attributes, ActiveDirectory, "GlobalCatalog1") + gc2Value := getkAttributeskeyValue(attributes, ActiveDirectory, "GlobalCatalog2") + gc3Value := getkAttributeskeyValue(attributes, ActiveDirectory, "GlobalCatalog3") + + if gc1Value == "" && gc2Value == "" && gc3Value == "" { + diags.AddError("Invalid GlobalCatalog configuration for Standard Schema", + "Atleast any one from GlobalCatalog1, GlobalCatalog2, GlobalCatalog3 must be configured for Disabled GCLookupEnable") + return false, diags + } + + gcRootDomain := checkAttributeskeyPresent(attributes, ActiveDirectory, "GCRootDomain") + if gcRootDomain { + diags.AddError("GCRootDomain can not be configured for Disabled GCLookupEnable", + "GCRootDomain can not be configured for Disabled GCLookupEnable") + return false, diags + } + } else { + diags.AddError("Invalid configuration for Standard Schema", "Please provide valid configuration for Standard Schema") + return false, diags + } + } + } + return true, diags +} + +// nolint: gocyclo, revive +func isValidDCLookupDomainConfig(ctx context.Context, prefix string, suffix string, attrsState *models.DirectoryServiceAuthProviderResource) (bool, diag.Diagnostics) { + var diags diag.Diagnostics + attributes := attrsState.ActiveDirectoryAttributes + check := checkAttributeskeyPresent(attributes, prefix, suffix) + if check { + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + var activeDirectoryPlan models.ActiveDirectoryResource + if !attrsState.ActiveDirectoryResource.IsNull() && !attrsState.ActiveDirectoryResource.IsUnknown() { + if diags := attrsState.ActiveDirectoryResource.As(ctx, &activeDirectoryPlan, objectAsOptions); diags.HasError() { + return false, diags + } + } + + var directoryPlan models.DirectoryResource + if !activeDirectoryPlan.Directory.IsNull() && !activeDirectoryPlan.Directory.IsUnknown() { + if diags := activeDirectoryPlan.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return false, diags + } + } + dcLookupEnableValue := getkAttributeskeyValue(attributes, ActiveDirectory, "DCLookupEnable") + serviceAddress := directoryPlan.ServiceAddresses + + serviceAddressList := make([]interface{}, 0) + for _, target := range serviceAddress { + serviceAddressList = append(serviceAddressList, target.ValueString()) + } + userDomain := checkAttributeskeyPresent(attributes, ActiveDirectory, "DCLookupByUserDomain") + specifyDomain := checkAttributeskeyPresent(attributes, ActiveDirectory, "DCLookupDomainName") + if dcLookupEnableValue == Disabled { + // diags.AddError("Invalid configuration for DCLookUp", "Service address must be configured for DCLookUp"+strconv.Itoa(len(serviceAddressList))) + if len(serviceAddressList) == 0 { + diags.AddError("ServiceAddresses is not Configured for Disabled DCLookUp", + "Atleast one Service address must be configured for Disabled DCLookUp") + return false, diags + } + if userDomain { + diags.AddError("DCLookupByUserDomain can not be Configured for Disabled DCLookUp", + "DCLookupByUserDomain can not be Configured for Disabled DCLookUp") + return false, diags + } + if specifyDomain { + diags.AddError("DCLookupDomainName can not be configured for Disabled DCLookUp", + "DCLookupDomainName can not be configured for Disabled DCLookUp") + return false, diags + } + } else if dcLookupEnableValue == Enabled { + if len(serviceAddressList) != 0 { + diags.AddError("Service address can not be configured for Enabled DCLookUp", "Service address can not be configured for Enabled DCLookUp") + return false, diags + } + if !userDomain { + diags.AddError("DCLookupByUserDomain must be configured for Enabled DCLookUp", + "DCLookupByUserDomain must be configured for Enabled DCLookUp") + return false, diags + } + userDomainValue := getkAttributeskeyValue(attributes, ActiveDirectory, "DCLookupByUserDomain") + if userDomainValue == Disabled { + if !specifyDomain { + diags.AddError("DCLookupDomainName must be configured for Disabled DCLookupByUserDomain", + "DCLookupDomainName must be configured for Disabled DCLookupByUserDomain") + return false, diags + } + specifyDomainValue := getkAttributeskeyValue(attributes, ActiveDirectory, "DCLookupDomainName") + if specifyDomainValue == "" { + diags.AddError("DCLookupDomainName must be configured for Disabled DCLookupByUserDomain", + "DCLookupDomainName must be configured for Disabled DCLookupByUserDomain") + return false, diags + } + } else if userDomainValue == Enabled { + specifyDomain := checkAttributeskeyPresent(attributes, ActiveDirectory, "DCLookupDomainName") + if specifyDomain { + diags.AddError("DCLookupDomainName can not be configured for Enabled DCLookupByUserDomain", + "DCLookupDomainName can not be configured for Enabled DCLookupByUserDomain") + return false, diags + } + } + return true, diags + + } else { + diags.AddError("Invalid configuration for DCLookUp", "Please provide valid configuration for DCLookUp") + return false, diags + } + } + return true, diags +} diff --git a/redfish/provider/resource_redfish_directory_service_auth_provider_schema.go b/redfish/provider/resource_redfish_directory_service_auth_provider_schema.go new file mode 100644 index 00000000..5326d5a1 --- /dev/null +++ b/redfish/provider/resource_redfish_directory_service_auth_provider_schema.go @@ -0,0 +1,244 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/objectvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +const ( + comma = " , " +) + +// DirectoryServiceAuthProviderResourceSchema defines the schema for the resource. +func DirectoryServiceAuthProviderResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the Directory Service Auth Provider resource", + Description: "ID of the Directory Service Auth Provider resource", + Computed: true, + }, + "active_directory": schema.SingleNestedAttribute{ + MarkdownDescription: "Active Directory" + noteADMessageInclusive + comma + noteMessageExclusive, + Description: "Active Directory" + noteADMessageInclusive + comma + noteMessageExclusive, + Attributes: ActiveDirectoryResourceSchema(), + Computed: true, + Optional: true, + Validators: []validator.Object{ + objectvalidator.AtLeastOneOf( + path.MatchRoot("ldap")), + objectvalidator.AlsoRequires( + path.MatchRoot("active_directory_attributes")), + }, + }, + "ldap": schema.SingleNestedAttribute{ + MarkdownDescription: "LDAP" + noteLDAPMessageInclusive + comma + noteMessageExclusive, + Description: "LDAP" + noteLDAPMessageInclusive + comma + noteMessageExclusive, + Attributes: LDAPResourceSchema(), + Computed: true, + Optional: true, + Validators: []validator.Object{ + objectvalidator.AtLeastOneOf( + path.MatchRoot("active_directory")), + objectvalidator.AlsoRequires( + path.MatchRoot("ldap_attributes")), + }, + }, + "active_directory_attributes": schema.MapAttribute{ + MarkdownDescription: "ActiveDirectory.* attributes in Dell iDRAC attributes." + noteADMessageInclusive + comma + + noteAttributesMessageExclusive, + Description: "ActiveDirectory.* attributes in Dell iDRAC attributes." + noteADMessageInclusive + comma + + noteAttributesMessageExclusive, + ElementType: types.StringType, + Computed: true, + Optional: true, + Validators: []validator.Map{ + mapvalidator.AlsoRequires( + path.MatchRoot("active_directory")), + mapvalidator.AtLeastOneOf( + path.MatchRoot("ldap_attributes")), + }, + }, + "ldap_attributes": schema.MapAttribute{ + MarkdownDescription: "LDAP.* attributes in Dell iDRAC attributes." + noteLDAPMessageInclusive + comma + noteAttributesMessageExclusive, + Description: "LDAP.* attributes in Dell iDRAC attributes." + noteLDAPMessageInclusive + comma + noteAttributesMessageExclusive, + ElementType: types.StringType, + Computed: true, + Optional: true, + Validators: []validator.Map{ + mapvalidator.AlsoRequires( + path.MatchRoot("ldap")), + mapvalidator.AtLeastOneOf( + path.MatchRoot("active_directory_attributes")), + }, + }, + } +} + +// ActiveDirectoryResourceSchema is a function that returns the schema for Active Directory Resource +func ActiveDirectoryResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "directory": schema.SingleNestedAttribute{ + MarkdownDescription: "Directory for Active Directory .", + Description: "Directory for Active Directory", + Attributes: DirectoryResourceSchema(), + Computed: true, + Optional: true, + }, + "authentication": schema.SingleNestedAttribute{ + MarkdownDescription: "Authentication information for the account provider.", + Description: "Authentication information for the account provider.", + Attributes: AuthenticationResourceSchema(), + Computed: true, + Optional: true, + }, + } +} + +// AuthenticationResourceSchema is a function that returns the schema for Authentication Resource +func AuthenticationResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "kerberos_key_tab_file": schema.StringAttribute{ + MarkdownDescription: "KerberosKeytab is a Base64-encoded version of the Kerberos keytab for this Service", + Description: "KerberosKeytab is a Base64-encoded version of the Kerberos keytab for this Service", + Computed: true, + Optional: true, + }, + } +} + +// RemoteRoleMappingResourceSchema is a function that returns the schema for Remote Role Mapping +func RemoteRoleMappingResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "remote_group": schema.StringAttribute{ + MarkdownDescription: "Name of the remote group.", + Description: "Name of the remote group.", + Computed: true, + Optional: true, + }, + "local_role": schema.StringAttribute{ + MarkdownDescription: "Role Assigned to the Group.", + Description: "Role Assigned to the Group.", + Computed: true, + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + stringvalidator.OneOf([]string{ + string("Administrator"), + string("Operator"), + string("ReadOnly"), + string("None"), + }...), + }, + }, + } +} + +// DirectoryResourceSchema is a function that returns the schema for Directory Resource +func DirectoryResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "remote_role_mapping": schema.ListNestedAttribute{ + MarkdownDescription: "Mapping rules that are used to convert the account providers account information to the local Redfish role", + Description: "Mapping rules that are used to convert the account providers account information to the local Redfish role", + NestedObject: schema.NestedAttributeObject{ + Attributes: RemoteRoleMappingResourceSchema(), + }, + Computed: true, + Optional: true, + }, + "service_addresses": schema.ListAttribute{ + MarkdownDescription: "ServiceAddresses of the account providers", + Description: "ServiceAddresses of the account providers", + Computed: true, + Optional: true, + ElementType: types.StringType, + }, + "service_enabled": schema.BoolAttribute{ + MarkdownDescription: "ServiceEnabled indicate whether this service is enabled.", + Description: "ServiceEnabled indicate whether this service is enabled.", + Computed: true, + Optional: true, + }, + } +} + +// LDAPResourceSchema is a function that returns the schema for LDAPResource +func LDAPResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "ldap_service": schema.SingleNestedAttribute{ + MarkdownDescription: "LDAPService is any additional mapping information needed to parse a generic LDAP service.", + Description: "LDAPService is any additional mapping information needed to parse a generic LDAP service.", + Attributes: LDAPServiceResourceSchema(), + Computed: true, + Optional: true, + }, + "directory": schema.SingleNestedAttribute{ + MarkdownDescription: "Directory for LDAP.", + Description: "Directory for LDAP", + Attributes: DirectoryResourceSchema(), + Computed: true, + Optional: true, + }, + } +} + +// LDAPServiceResourceSchema is a function that returns the schema for LDAPService Resource +func LDAPServiceResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "search_settings": schema.SingleNestedAttribute{ + MarkdownDescription: "SearchSettings is the required settings to search an external LDAP service.", + Description: "SearchSettings is the required settings to search an external LDAP service.", + Attributes: SearchSettingsResourceSchema(), + Computed: true, + Optional: true, + }, + } +} + +// SearchSettingsResourceSchema is a function that returns the schema for SearchSettings Resource +func SearchSettingsResourceSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "base_distinguished_names": schema.ListAttribute{ + MarkdownDescription: "BaseDistinguishedNames is an array of base distinguished names to use to search an external LDAP service.", + Description: "BaseDistinguishedNames is an array of base distinguished names to use to search an external LDAP service.", + Computed: true, + Optional: true, + ElementType: types.StringType, + }, + + "user_name_attribute": schema.StringAttribute{ + MarkdownDescription: "UsernameAttribute is the attribute name that contains the LDAP user name.", + Description: "UsernameAttribute is the attribute name that contains the LDAP user name.", + Computed: true, + Optional: true, + }, + + "group_name_attribute": schema.StringAttribute{ + MarkdownDescription: "GroupNameAttribute is the attribute name that contains the LDAP group name.", + Description: "GroupNameAttribute is the attribute name that contains the LDAP group name.", + Computed: true, + Optional: true, + }, + } +} diff --git a/redfish/provider/resource_redfish_directory_service_auth_provider_schema_helper.go b/redfish/provider/resource_redfish_directory_service_auth_provider_schema_helper.go new file mode 100644 index 00000000..8228a7f9 --- /dev/null +++ b/redfish/provider/resource_redfish_directory_service_auth_provider_schema_helper.go @@ -0,0 +1,723 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "context" + "fmt" + "strings" + "terraform-provider-redfish/redfish/models" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stmcginnis/gofish" + "github.com/stmcginnis/gofish/redfish" +) + +const ( + ldapService = "ldap_service" + baseDistinguishedNames = "base_distinguished_names" + authentication = "authentication" + kerberosKeyTabFile = "kerberos_key_tab_file" + localRole = "local_role" + remoteGroup = "remote_group" + directory = "directory" + remoteRoleMapping = "remote_role_mapping" + serviceAddresses = "service_addresses" + serviceEnabled = "service_enabled" + searchSettings = "search_settings" +) + +func getActiveDirectoryModelType() map[string]attr.Type { + return map[string]attr.Type{ + directory: types.ObjectType{AttrTypes: getDirectoryModelType()}, + authentication: types.ObjectType{AttrTypes: getAuthentcationModelType()}, + } +} + +func getLDAPModelType() map[string]attr.Type { + return map[string]attr.Type{ + directory: types.ObjectType{AttrTypes: getDirectoryModelType()}, + ldapService: types.ObjectType{AttrTypes: getLDAPServiceModelType()}, + } +} + +func getAuthentcationModelType() map[string]attr.Type { + return map[string]attr.Type{ + kerberosKeyTabFile: types.StringType, + } +} + +func getDirectoryModelType() map[string]attr.Type { + return map[string]attr.Type{ + remoteRoleMapping: types.ListType{ElemType: types.ObjectType{AttrTypes: getRemoteRoleMappingModelType()}}, + serviceAddresses: types.ListType{ElemType: types.StringType}, + serviceEnabled: types.BoolType, + } +} + +func getLDAPServiceModelType() map[string]attr.Type { + return map[string]attr.Type{ + searchSettings: types.ObjectType{AttrTypes: getSearchSettingsModelType()}, + } +} + +func getSearchSettingsModelType() map[string]attr.Type { + return map[string]attr.Type{ + baseDistinguishedNames: types.ListType{ElemType: types.StringType}, + "user_name_attribute": types.StringType, + "group_name_attribute": types.StringType, + } +} + +func getRemoteRoleMappingModelType() map[string]attr.Type { + return map[string]attr.Type{ + remoteGroup: types.StringType, + localRole: types.StringType, + } +} + +// nolint: revive +func getRemoteRoleMappingObjectValue(ctx context.Context, service []redfish.RoleMapping, state *models.DirectoryResource) ([]attr.Value, diag.Diagnostics) { + var diags diag.Diagnostics + remoteRoleMapList := make([]attr.Value, 0) + var oldRemoteRolMap []models.RemoteRoleMapping + if !state.RemoteRoleMapping.IsNull() && !state.RemoteRoleMapping.IsUnknown() { + if diags := state.RemoteRoleMapping.ElementsAs(ctx, &oldRemoteRolMap, true); diags.HasError() { + diags.AddError("oldRemoteRolMap nil ", "oldRemoteRolMap nil") + return remoteRoleMapList, diags + } + } + + for _, oldElement := range oldRemoteRolMap { + for _, serviceElement := range service { + if serviceElement.LocalRole == oldElement.LocalRole.ValueString() && serviceElement.RemoteGroup == oldElement.RemoteGroup.ValueString() { + remoteRoleItemMap := map[string]attr.Value{ + remoteGroup: types.StringValue(serviceElement.RemoteGroup), + localRole: types.StringValue(serviceElement.LocalRole), + } + remoteRoleItemObj, diags := types.ObjectValue(getRemoteRoleMappingModelType(), remoteRoleItemMap) + if diags.HasError() { + return remoteRoleMapList, diags + } + remoteRoleMapList = append(remoteRoleMapList, remoteRoleItemObj) + break + } + } + } + if len(remoteRoleMapList) == 0 { + for _, serviceElement := range service { + remoteRoleItemMap := map[string]attr.Value{ + remoteGroup: types.StringValue(serviceElement.RemoteGroup), + localRole: types.StringValue(serviceElement.LocalRole), + } + remoteRoleItemObj, diags := types.ObjectValue(getRemoteRoleMappingModelType(), remoteRoleItemMap) + if diags.HasError() { + return remoteRoleMapList, diags + } + remoteRoleMapList = append(remoteRoleMapList, remoteRoleItemObj) + } + } + + return remoteRoleMapList, diags +} + +// nolint: revive +func getActiveDirectoryObjectValue(ctx context.Context, service *redfish.AccountService, state *models.DirectoryServiceAuthProviderResource, objectAsOptions basetypes.ObjectAsOptions) (basetypes.ObjectValue, diag.Diagnostics) { + emptyObj := types.ObjectNull(getDirectoryModelType()) + var oldActiveDirRes models.ActiveDirectoryResource + if !state.ActiveDirectoryResource.IsNull() && !state.ActiveDirectoryResource.IsUnknown() { + if diags := state.ActiveDirectoryResource.As(ctx, &oldActiveDirRes, objectAsOptions); diags.HasError() { + diags.AddError("oldActiveDirRes nill ", "oldActiveDirRes nil") + return emptyObj, diags + } + } + var directoryPlan models.DirectoryResource + if !oldActiveDirRes.Directory.IsNull() && !oldActiveDirRes.Directory.IsUnknown() { + if diags := oldActiveDirRes.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + diags.AddError("directoryPlan nill ", "directoryPlan nil") + return emptyObj, diags + } + } + serviceAddress := directoryPlan.ServiceAddresses + activeDIrectoryRoleMappingList := service.ActiveDirectory.RemoteRoleMapping + remoteRoleMappingObj, diags := getRemoteRoleMappingObjectValue(ctx, activeDIrectoryRoleMappingList, &directoryPlan) + if diags.HasError() { + return emptyObj, diags + } + + remoteRoleList, diags := types.ListValue(types.ObjectType{AttrTypes: getRemoteRoleMappingModelType()}, remoteRoleMappingObj) + + if diags.HasError() { + return emptyObj, diags + } + serviceAddressList, diags := getConfigDataList(service.ActiveDirectory.ServiceAddresses, serviceAddress) + + if diags.HasError() { + return emptyObj, diags + } + + directoryMap := map[string]attr.Value{ + remoteRoleMapping: remoteRoleList, + serviceAddresses: serviceAddressList, + serviceEnabled: types.BoolValue(service.ActiveDirectory.ServiceEnabled), + } + return types.ObjectValue(getDirectoryModelType(), directoryMap) +} + +func getConfigDataList(input []string, stateServiceAddress []types.String) (basetypes.ListValue, diag.Diagnostics) { + out := make([]attr.Value, 0) + if len(stateServiceAddress) != 0 { + for _, stateInput := range stateServiceAddress { + for _, i := range input { + if stateInput.ValueString() == i { + out = append(out, types.StringValue(i)) + break + } + } + } + } + + if len(out) == 0 { + for _, target := range input { + out = append(out, types.StringValue(target)) + } + } + + return types.ListValue(types.StringType, out) +} + +// nolint: revive +func getLDAPDirectoryObjectValue(ctx context.Context, service *redfish.AccountService, state *models.DirectoryServiceAuthProviderResource, objectAsOptions basetypes.ObjectAsOptions) (basetypes.ObjectValue, diag.Diagnostics) { + emptyObj := types.ObjectNull(getDirectoryModelType()) + + var oldLDAPRes models.LDAPResource + if !state.LDAPResource.IsNull() && !state.LDAPResource.IsUnknown() { + if diags := state.LDAPResource.As(ctx, &oldLDAPRes, objectAsOptions); diags.HasError() { + return emptyObj, diags + } + } + + var directoryPlan models.DirectoryResource + if !oldLDAPRes.Directory.IsNull() && !oldLDAPRes.Directory.IsUnknown() { + if diags := oldLDAPRes.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return emptyObj, diags + } + } + + serviceAddress := directoryPlan.ServiceAddresses + ldapRemoteRoleList := service.LDAP.RemoteRoleMapping + + remoteRoleMappingObj, diags := getRemoteRoleMappingObjectValue(ctx, ldapRemoteRoleList, &directoryPlan) + if diags.HasError() { + return emptyObj, diags + } + + remoteRoleList, diags := types.ListValue(types.ObjectType{AttrTypes: getRemoteRoleMappingModelType()}, remoteRoleMappingObj) + + if diags.HasError() { + return emptyObj, diags + } + + serviceAddressList, diags := getConfigDataList(service.LDAP.ServiceAddresses, serviceAddress) + if diags.HasError() { + return emptyObj, diags + } + + directoryMap := map[string]attr.Value{ + remoteRoleMapping: remoteRoleList, + serviceAddresses: serviceAddressList, + serviceEnabled: types.BoolValue(service.LDAP.ServiceEnabled), + } + + return types.ObjectValue(getDirectoryModelType(), directoryMap) +} + +// nolint: revive +func getLDAPServiceObjectValue(ctx context.Context, service *redfish.AccountService, state *models.DirectoryServiceAuthProviderResource, objectAsOptions basetypes.ObjectAsOptions) (basetypes.ObjectValue, diag.Diagnostics) { + emptyObj := types.ObjectNull(getLDAPServiceModelType()) + + var oldLDAPRes models.LDAPResource + if !state.LDAPResource.IsNull() && !state.LDAPResource.IsUnknown() { + if diags := state.LDAPResource.As(ctx, &oldLDAPRes, objectAsOptions); diags.HasError() { + return emptyObj, diags + } + } + + var oldLDAPService models.LDAPService + if !oldLDAPRes.LDAPService.IsNull() && !oldLDAPRes.LDAPService.IsUnknown() { + if diags := oldLDAPRes.LDAPService.As(ctx, &oldLDAPService, objectAsOptions); diags.HasError() { + return emptyObj, diags + } + } + ldapSearchSetting, diags := getLDAPSearchSettingsObjectValue(ctx, service, state, objectAsOptions) + + if diags.HasError() { + return emptyObj, diags + } + + ldapServiceMap := map[string]attr.Value{ + searchSettings: ldapSearchSetting, + } + + return types.ObjectValue(getLDAPServiceModelType(), ldapServiceMap) +} + +// nolint: revive +func getLDAPSearchSettingsObjectValue(ctx context.Context, service *redfish.AccountService, state *models.DirectoryServiceAuthProviderResource, objectAsOptions basetypes.ObjectAsOptions) (basetypes.ObjectValue, diag.Diagnostics) { + emptyObj := types.ObjectNull(getSearchSettingsModelType()) + var oldLDAPRes models.LDAPResource + if !state.LDAPResource.IsNull() && !state.LDAPResource.IsUnknown() { + if diags := state.LDAPResource.As(ctx, &oldLDAPRes, objectAsOptions); diags.HasError() { + return emptyObj, diags + } + } + var oldLDAPService models.LDAPServiceResource + if !oldLDAPRes.LDAPService.IsNull() && !oldLDAPRes.LDAPService.IsUnknown() { + if diags := oldLDAPRes.LDAPService.As(ctx, &oldLDAPService, objectAsOptions); diags.HasError() { + return emptyObj, diags + } + } + var oldSearchSettings models.SearchSettingsResource + if !oldLDAPService.SearchSettings.IsNull() && !oldLDAPService.SearchSettings.IsUnknown() { + if diags := oldLDAPService.SearchSettings.As(ctx, &oldSearchSettings, objectAsOptions); diags.HasError() { + return emptyObj, diags + } + } + + baseDistinguished := oldSearchSettings.BaseDistinguishedNames + baseDistinguishedList, diags := getConfigDataList(service.LDAP.LDAPService.SearchSettings.BaseDistinguishedNames, baseDistinguished) + if diags.HasError() { + return emptyObj, diags + } + + searchSettingsMap := map[string]attr.Value{ + baseDistinguishedNames: baseDistinguishedList, + "user_name_attribute": types.StringValue(service.LDAP.LDAPService.SearchSettings.UsernameAttribute), + "group_name_attribute": types.StringValue(service.LDAP.LDAPService.SearchSettings.GroupNameAttribute), + } + + return types.ObjectValue(getSearchSettingsModelType(), searchSettingsMap) +} + +// nolint: revive +func getAuthentcationObjectValue(ctx context.Context, service *redfish.AccountService, directoryPlan models.ActiveDirectoryResource, objectAsOptions basetypes.ObjectAsOptions) (basetypes.ObjectValue, diag.Diagnostics) { + var authenticationPlan models.AuthenticationResource + var authentication map[string]attr.Value + emptyObj := types.ObjectNull(getAuthentcationModelType()) + if !directoryPlan.Authentication.IsNull() && !directoryPlan.Authentication.IsUnknown() { + diags := directoryPlan.Authentication.As(ctx, &authenticationPlan, objectAsOptions) + if diags.HasError() { + return emptyObj, diags + } + authentication = map[string]attr.Value{ + kerberosKeyTabFile: types.StringValue(authenticationPlan.KerberosKeytab.ValueString()), + } + } + + if directoryPlan.Authentication.IsNull() || directoryPlan.Authentication.IsUnknown() { + authentication = map[string]attr.Value{ + kerberosKeyTabFile: types.StringValue(service.ActiveDirectory.Authentication.KerberosKeytab), + } + } + return types.ObjectValue(getAuthentcationModelType(), authentication) +} + +// nolint: gocyclo, revive +func parseActiveDirectoryIntoState(ctx context.Context, acctService *redfish.AccountService, service *gofish.Service, state *models.DirectoryServiceAuthProviderResource) (diags diag.Diagnostics) { + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + var oldActiveDirectory models.ActiveDirectoryResource + if !state.ActiveDirectoryResource.IsNull() && !state.ActiveDirectoryResource.IsUnknown() { + diags = state.ActiveDirectoryResource.As(ctx, &oldActiveDirectory, objectAsOptions) + if diags.HasError() { + return diags + } + + } + directoryObj, diags := getActiveDirectoryObjectValue(ctx, acctService, state, objectAsOptions) + if diags.HasError() { + return diags + } + + authenticationObj, diags := getAuthentcationObjectValue(ctx, acctService, oldActiveDirectory, objectAsOptions) + if diags.HasError() { + return diags + } + var idracAttributesPlan models.DellIdracAttributes + if !state.ActiveDirectoryAttributes.IsNull() && !state.ActiveDirectoryAttributes.IsUnknown() { + idracAttributesPlan.Attributes = state.ActiveDirectoryAttributes + } + if diags := readRedfishDellIdracAttributes(ctx, service, &idracAttributesPlan); diags.HasError() { + return diags + } + var activeDirAttributes types.Map + if !state.ActiveDirectoryAttributes.IsNull() && !state.ActiveDirectoryAttributes.IsUnknown() { + activeDirAttributes = state.ActiveDirectoryAttributes + } + + if state.ActiveDirectoryAttributes.IsNull() || state.ActiveDirectoryAttributes.IsUnknown() { + // nolint: gocyclo, gocognit,revive + activeDirectoryAttributes := []string{".CertValidationEnable", ".SSOEnable", ".AuthTimeout", ".DCLookupEnable", ".DCLookupByUserDomain", ".DCLookupDomainName", ".Schema", ".GCLookupEnable", ".GCRootDomain", ".GlobalCatalog1", ".GlobalCatalog2", ".GlobalCatalog3", ".RacName", ".RacDomain", ".RSASecurID2FAAD"} + + attributesToReturn := make(map[string]attr.Value) + for k, v := range idracAttributesPlan.Attributes.Elements() { + if strings.HasPrefix(k, "ActiveDirectory.") { + for _, input := range activeDirectoryAttributes { + if strings.HasSuffix(k, input) { + attributesToReturn[k] = v + } + } + } + // nolint: revive + if (strings.HasPrefix(k, "UserDomain.") && strings.HasSuffix(k, ".Name")) || (strings.HasPrefix(k, "ADGroup.") && strings.HasSuffix(k, ".Name")) { + attributesToReturn[k] = v + } + + if strings.HasPrefix(k, "RSASecurID2FA.") && (strings.HasSuffix(k, ".RSASecurIDAuthenticationServer") || strings.HasSuffix(k, ".RSASecurIDAccessKey") || strings.HasSuffix(k, ".RSASecurIDClientID")) { + attributesToReturn[k] = v + } + } + + activeDirAttributes = types.MapValueMust(types.StringType, attributesToReturn) + } + activeDirectoryMap := map[string]attr.Value{ + directory: directoryObj, + authentication: authenticationObj, + } + state.ActiveDirectoryAttributes = activeDirAttributes + state.ActiveDirectoryResource, diags = types.ObjectValue(getActiveDirectoryModelType(), activeDirectoryMap) + + return diags +} + +// nolint:gocyclo, revive +func parseLDAPIntoState(ctx context.Context, acctService *redfish.AccountService, service *gofish.Service, state *models.DirectoryServiceAuthProviderResource) (diags diag.Diagnostics) { + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + var oldLDAP models.LDAPResource + if !state.LDAPResource.IsNull() && !state.LDAPResource.IsUnknown() { + if diags := state.LDAPResource.As(ctx, &oldLDAP, objectAsOptions); diags.HasError() { + return diags + } + } + + directoryObj, diags := getLDAPDirectoryObjectValue(ctx, acctService, state, objectAsOptions) + if diags.HasError() { + return diags + } + + ldapServiceObj, diags := getLDAPServiceObjectValue(ctx, acctService, state, objectAsOptions) + + if diags.HasError() { + return diags + } + var idracAttributesPlan models.DellIdracAttributes + if !state.LDAPAttributes.IsNull() && !state.LDAPAttributes.IsUnknown() { + idracAttributesPlan.Attributes = state.LDAPAttributes + } + + if diags = readRedfishDellIdracAttributes(ctx, service, &idracAttributesPlan); diags.HasError() { + return diags + } + + var ldapDirAttributes types.Map + if !state.LDAPAttributes.IsNull() && !state.LDAPAttributes.IsUnknown() { + ldapDirAttributes = state.LDAPAttributes + } + + if state.LDAPAttributes.IsNull() || state.LDAPAttributes.IsUnknown() { + // nolint: gocyclo, gocognit,revive + ldapAttributes := []string{".CertValidationEnable", ".GroupAttributeIsDN", ".Port", ".BindDN", ".BindPassword", ".SearchFilter", ".RSASecurID2FALDAP"} + attributesToReturn := make(map[string]attr.Value) + for k, v := range idracAttributesPlan.Attributes.Elements() { + if strings.HasPrefix(k, "LDAP.") { + for _, input := range ldapAttributes { + if strings.HasSuffix(k, input) { + attributesToReturn[k] = v + } + } + } + + if strings.HasPrefix(k, "RSASecurID2FA.") && (strings.HasSuffix(k, ".RSASecurIDAuthenticationServer") || + strings.HasSuffix(k, ".RSASecurIDAccessKey") || strings.HasSuffix(k, ".RSASecurIDClientID")) { + attributesToReturn[k] = v + } + } + ldapDirAttributes = types.MapValueMust(types.StringType, attributesToReturn) + } + ldapMap := map[string]attr.Value{ + directory: directoryObj, + ldapService: ldapServiceObj, + } + state.LDAPResource, diags = types.ObjectValue(getLDAPModelType(), ldapMap) + state.LDAPAttributes = ldapDirAttributes + return diags +} + +// nolint: gocyclo, revive +func getActiveDirectoryPatchBody(ctx context.Context, attrsState *models.DirectoryServiceAuthProviderResource) (map[string]interface{}, diag.Diagnostics) { + // var diags diag.Diagnostics + supportedActiveDirectory := map[string]string{ + serviceEnabled: "ServiceEnabled", + serviceAddresses: "ServiceAddresses", + remoteRoleMapping: "RemoteRoleMapping", + authentication: "Authentication", + } + supportedRemoteRoleMappingParams := map[string]string{ + remoteGroup: "RemoteGroup", + localRole: "LocalRole", + } + + supportedAuthentication := map[string]string{ + kerberosKeyTabFile: "KerberosKeytab", + } + + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + + var activeDirectoryPlan models.ActiveDirectoryResource + if !attrsState.ActiveDirectoryResource.IsNull() && !attrsState.ActiveDirectoryResource.IsUnknown() { + if diags := attrsState.ActiveDirectoryResource.As(ctx, &activeDirectoryPlan, objectAsOptions); diags.HasError() { + return nil, diags + } + } + + var directoryPlan models.DirectoryResource + if !activeDirectoryPlan.Directory.IsNull() && !activeDirectoryPlan.Directory.IsUnknown() { + if diags := activeDirectoryPlan.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return nil, diags + } + } + var authenticationPlan models.AuthenticationResource + if !activeDirectoryPlan.Authentication.IsNull() && !activeDirectoryPlan.Authentication.IsUnknown() { + if diags := activeDirectoryPlan.Authentication.As(ctx, &authenticationPlan, objectAsOptions); diags.HasError() { + return nil, diags + } + } + + patchBody := make(map[string]interface{}) + if !activeDirectoryPlan.Directory.IsNull() && !activeDirectoryPlan.Directory.IsUnknown() { + for key, value := range activeDirectoryPlan.Directory.Attributes() { + if !value.IsUnknown() && !value.IsNull() { + goValue, err := convertTerraformValueToGoBasicValue(ctx, value) + if err != nil { + tflog.Trace(ctx, fmt.Sprintf("Failed to convert Ethernet value to go value: %s", err.Error())) + continue + } + if fieldName, ok := supportedActiveDirectory[key]; ok { + patchBody[fieldName] = goValue + } + } + } + } + + // get list of remote role mapping + if !directoryPlan.RemoteRoleMapping.IsNull() && !directoryPlan.RemoteRoleMapping.IsUnknown() { + var remoteRoleMappingModel []models.RemoteRoleMapping + + if diags := directoryPlan.RemoteRoleMapping.ElementsAs(ctx, &remoteRoleMappingModel, true); diags.HasError() { + return nil, diags + } + + remoteRoleMappingList := make([]interface{}, 0) + for _, target := range remoteRoleMappingModel { + remoteRoleMappingBody := make(map[string]interface{}) + if !target.LocalRole.IsNull() && !target.LocalRole.IsUnknown() { + remoteRoleMappingBody[supportedRemoteRoleMappingParams[localRole]] = target.LocalRole.ValueString() + } + if !target.RemoteGroup.IsNull() && !target.RemoteGroup.IsUnknown() { + remoteRoleMappingBody[supportedRemoteRoleMappingParams[remoteGroup]] = target.RemoteGroup.ValueString() + } + if len(remoteRoleMappingBody) > 0 { + remoteRoleMappingList = append(remoteRoleMappingList, remoteRoleMappingBody) + } + } + + patchBody[supportedActiveDirectory[remoteRoleMapping]] = remoteRoleMappingList + } + + serviceAddress := directoryPlan.ServiceAddresses + serviceAddressList := make([]interface{}, 0) + for _, target := range serviceAddress { + serviceAddressList = append(serviceAddressList, target.ValueString()) + } + if len(serviceAddressList) != 0 { + patchBody[supportedActiveDirectory[serviceAddresses]] = serviceAddressList + } + + // get directory patch body + if !activeDirectoryPlan.Authentication.IsNull() && !activeDirectoryPlan.Authentication.IsUnknown() { + authenticationPatchBody := make(map[string]interface{}) + for key, value := range activeDirectoryPlan.Authentication.Attributes() { + if !value.IsUnknown() && !value.IsNull() { + goValue, err := convertTerraformValueToGoBasicValue(ctx, value) + if err != nil { + tflog.Trace(ctx, fmt.Sprintf("Failed to convert VLAN value to go value: %s", err.Error())) + continue + } + if fieldName, ok := supportedAuthentication[key]; ok { + authenticationPatchBody[fieldName] = goValue + } + } + } + patchBody[supportedActiveDirectory[authentication]] = authenticationPatchBody + } + + return patchBody, nil +} + +// nolint: gocyclo, revive +func getLDAPPatchBody(ctx context.Context, attrsState *models.DirectoryServiceAuthProviderResource) (map[string]interface{}, diag.Diagnostics) { + // var diags diag.Diagnostics + supportedLDAP := map[string]string{ + serviceEnabled: "ServiceEnabled", + serviceAddresses: "ServiceAddresses", + remoteRoleMapping: "RemoteRoleMapping", + ldapService: "LDAPService", + } + supportedRemoteRoleMappingParams := map[string]string{ + remoteGroup: "RemoteGroup", + localRole: "LocalRole", + } + + supportedLDAPService := map[string]string{ + searchSettings: "SearchSettings", + } + + supportedSearchSetting := map[string]string{ + baseDistinguishedNames: "BaseDistinguishedNames", + "user_name_attribute": "UsernameAttribute", + "group_name_attribute": "GroupNameAttribute", + } + + objectAsOptions := basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true} + + var ldapPlan models.LDAPResource + if !attrsState.LDAPResource.IsNull() && !attrsState.LDAPResource.IsUnknown() { + if diags := attrsState.LDAPResource.As(ctx, &ldapPlan, objectAsOptions); diags.HasError() { + return nil, diags + } + } + + var ldapServicePlan models.LDAPServiceResource + if !ldapPlan.LDAPService.IsNull() && !ldapPlan.LDAPService.IsUnknown() { + if diags := ldapPlan.LDAPService.As(ctx, &ldapServicePlan, objectAsOptions); diags.HasError() { + return nil, diags + } + } + + var ldapSearchSettingsPlan models.SearchSettingsResource + if !ldapServicePlan.SearchSettings.IsNull() && !ldapServicePlan.SearchSettings.IsUnknown() { + if diags := ldapServicePlan.SearchSettings.As(ctx, &ldapSearchSettingsPlan, objectAsOptions); diags.HasError() { + return nil, diags + } + } + var directoryPlan models.DirectoryResource + if !ldapPlan.Directory.IsNull() && !ldapPlan.Directory.IsUnknown() { + if diags := ldapPlan.Directory.As(ctx, &directoryPlan, objectAsOptions); diags.HasError() { + return nil, diags + } + } + patchBody := make(map[string]interface{}) + if !ldapPlan.Directory.IsNull() && !ldapPlan.Directory.IsUnknown() { + for key, value := range ldapPlan.Directory.Attributes() { + if !value.IsUnknown() && !value.IsNull() { + goValue, err := convertTerraformValueToGoBasicValue(ctx, value) + if err != nil { + tflog.Trace(ctx, fmt.Sprintf("Failed to convert Ethernet value to go value: %s", err.Error())) + continue + } + if fieldName, ok := supportedLDAP[key]; ok { + patchBody[fieldName] = goValue + } + } + } + } + + if !ldapPlan.LDAPService.IsNull() && !ldapPlan.LDAPService.IsUnknown() { + ldapServicepatchBody := make(map[string]interface{}) + for key1, value1 := range ldapPlan.LDAPService.Attributes() { + if !value1.IsUnknown() && !value1.IsNull() { + if !ldapServicePlan.SearchSettings.IsNull() && !ldapServicePlan.SearchSettings.IsUnknown() { + ldapSearchSettingPatchBody := make(map[string]interface{}) + for key, value := range ldapServicePlan.SearchSettings.Attributes() { + if !value.IsUnknown() && !value.IsNull() { + goValue, err := convertTerraformValueToGoBasicValue(ctx, value) + if err != nil { + tflog.Trace(ctx, fmt.Sprintf("Failed to convert Ethernet value to go value: %s", err.Error())) + continue + } + if fieldName, ok := supportedSearchSetting[key]; ok { + ldapSearchSettingPatchBody[fieldName] = goValue + } + } + } + baseDiss := ldapSearchSettingsPlan.BaseDistinguishedNames + baseDissList := make([]interface{}, 0) + for _, target := range baseDiss { + baseDissList = append(baseDissList, target.ValueString()) + } + if len(baseDissList) != 0 { + ldapSearchSettingPatchBody[supportedSearchSetting[baseDistinguishedNames]] = baseDissList + } + + if fieldName, ok := supportedLDAPService[key1]; ok { + ldapServicepatchBody[fieldName] = ldapSearchSettingPatchBody + } + } + } + } + patchBody[supportedLDAP[ldapService]] = ldapServicepatchBody + } + + // get list of remote role mapping + if !directoryPlan.RemoteRoleMapping.IsNull() && !directoryPlan.RemoteRoleMapping.IsUnknown() { + var remoteRoleMappingModel []models.RemoteRoleMapping + + if diags := directoryPlan.RemoteRoleMapping.ElementsAs(ctx, &remoteRoleMappingModel, true); diags.HasError() { + return nil, diags + } + + remoteRoleMappingList := make([]interface{}, 0) + for _, target := range remoteRoleMappingModel { + remoteRoleMappingBody := make(map[string]interface{}) + if !target.LocalRole.IsNull() && !target.LocalRole.IsUnknown() { + remoteRoleMappingBody[supportedRemoteRoleMappingParams[localRole]] = target.LocalRole.ValueString() + } + if !target.RemoteGroup.IsNull() && !target.RemoteGroup.IsUnknown() { + remoteRoleMappingBody[supportedRemoteRoleMappingParams[remoteGroup]] = target.RemoteGroup.ValueString() + } + if len(remoteRoleMappingBody) > 0 { + remoteRoleMappingList = append(remoteRoleMappingList, remoteRoleMappingBody) + } + } + + patchBody[supportedLDAP[remoteRoleMapping]] = remoteRoleMappingList + } + + serviceAddress := directoryPlan.ServiceAddresses + serviceAddressList := make([]interface{}, 0) + for _, target := range serviceAddress { + serviceAddressList = append(serviceAddressList, target.ValueString()) + } + if len(serviceAddressList) != 0 { + patchBody[supportedLDAP[serviceAddresses]] = serviceAddressList + } + return patchBody, nil +} diff --git a/redfish/provider/resource_redfish_directory_service_auth_provider_test.go b/redfish/provider/resource_redfish_directory_service_auth_provider_test.go new file mode 100644 index 00000000..d9c28f93 --- /dev/null +++ b/redfish/provider/resource_redfish_directory_service_auth_provider_test.go @@ -0,0 +1,1269 @@ +/* +Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provider + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccRedfishDirectoryServiceAuthProviderBasic(t *testing.T) { + terraformDSAuthProviderResourceName := "redfish_directory_service_auth_provider.ds_auth" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + // error create with both `ActiveDirectory` and `LDAP` + Config: testAccRedfishDirectoryServiceAuthProviderErrorConfig(creds), + ExpectError: regexp.MustCompile("Error when creating both of `ActiveDirectory` and `LDAP`"), + }, + + { + // create with `ActiveDirectory` + Config: testAccRedfishDirectoryServiceAuthProviderADConfig(creds), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(terraformDSAuthProviderResourceName, "active_directory.directory.service_enabled", "true"), + ), + }, + + { + // update with `LDAP` + Config: testAccRedfishDirectoryServiceAuthProviderLDAPConfig(creds), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(terraformDSAuthProviderResourceName, "ldap.directory.service_enabled", "false"), + ), + }, + { + // update with `ActiveDirectory` + Config: testAccRedfishDirectoryServiceAuthProviderAD_UpdateConfig(creds), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(terraformDSAuthProviderResourceName, "active_directory.directory.service_enabled", "false"), + resource.TestCheckResourceAttr(terraformDSAuthProviderResourceName, "active_directory_attributes.ActiveDirectory.1.AuthTimeout", "130"), + ), + }, + }, + }) +} + +func TestAccRedfishDirectoryServiceAuthProviderInvalidCase(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + // error for empty AuthTimeout in ActiveDirectory + Config: testAccRedfishDirectoryServiceAuthProviderEmptyAuth(creds), + ExpectError: regexp.MustCompile("Invalid AuthTimeout, Please provide all the required configuration"), + }, + + { + // error for Invalid AuthTimeout in ActiveDirectory + Config: testAccRedfishDirectoryServiceAuthProviderInvalidAuth(creds), + ExpectError: regexp.MustCompile("Invalid AuthTimeout, AuthTimeout must be between 15 and 300"), + }, + { + // error ActiveDirectoryService Disabled and SSOEnable Enabled + Config: testAccRedfishDirectoryServiceAuthProviderADDisableSSOEnable(creds), + ExpectError: regexp.MustCompile("Please provide valid Configuration for SSO"), + }, + + { + // error ActiveDirectoryService Enabled and SSOEnable Enabled and no Kerberos key tab + Config: testAccRedfishDirectoryServiceAuthProviderADEnSSOEnNoKb(creds), + ExpectError: regexp.MustCompile("Please provide valid kerberos key tab file when SSO is enabled "), + }, + + { + // error DCLookupEnable Enabled and service address as empty + Config: testAccRedfishDirectoryServiceAuthProviderDClookUpEnServiceAddEmpty(creds), + ExpectError: regexp.MustCompile("ServiceAddresses is not Configured for Disabled DCLookUp"), + }, + + { + // error DCLookupEnable Disabled and DCLookupByUserDomain config + Config: testAccRedfishDirectoryServiceAuthProviderDCLookupByUserDomainConfig(creds), + ExpectError: regexp.MustCompile("DCLookupByUserDomain can not be Configured for Disabled DCLookUp"), + }, + + { + // error DCLookupEnable Disabled and DCLookupDomainName config + Config: testAccRedfishDirectoryServiceAuthProviderDCLookupDomainNameConfig(creds), + ExpectError: regexp.MustCompile("DCLookupDomainName can not be configured for Disabled DCLookUp"), + }, + + { + // error DCLookupEnable Enabled and ServiceAddress non empty + Config: testAccRedfishDirectoryServiceAuthProviderDDCLookupEnableNoServiceAddConfig(creds), + ExpectError: regexp.MustCompile("Service address can not be configured for Enabled DCLookUp"), + }, + + { + // error DCLookupEnable Enabled and DCLookupByUserDomain does not exist + Config: testAccRedfishDirectoryServiceAuthProviderDCLookupByUserDomainEmptyConfig(creds), + ExpectError: regexp.MustCompile("DCLookupByUserDomain must be configured for Enabled DCLookUp"), + }, + + { + // error DCLookupEnable Enabled, DCLookupByUserDomain Disabled and DCLookupDomainName Empty + Config: testAccRedfishDirectoryServiceAuthProviderDCLookupDomainNameEmptyConfig(creds), + ExpectError: regexp.MustCompile("DCLookupDomainName must be configured for Disabled DCLookupByUserDomain"), + }, + + { + // error DCLookupEnable Enabled DCLookupByUserDomain Enabled and DCLookupDomainName non Empty + Config: testAccRedfishDirectoryServiceAuthProviderDCLookupEnableDCLookupDomainNameConfig(creds), + ExpectError: regexp.MustCompile("DCLookupDomainName can not be configured for Enabled DCLookupByUserDomain"), + }, + }, + }) +} + +func TestAccRedfishDirectoryServiceAuthProviderInvalidSchema_Config(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + // error Extended Schema without RacName and RacDomain + Config: testAccRedfishDirectoryServiceAuthProviderExtendedNoRacConfig(creds), + ExpectError: regexp.MustCompile("Please provide valid config for RacName and RacDomain"), + }, + { + // error Extended Schema with empty RacName and RacDomain + Config: testAccRedfishDirectoryServiceAuthProviderExtendedEmptyRacConfig(creds), + ExpectError: regexp.MustCompile("RacName and RacDomain can not be null for Extended Schema"), + }, + { + // error Extended Schema with GCLookup config + Config: testAccRedfishDirectoryServiceAuthProviderExtendedGCLookUpConfig(creds), + ExpectError: regexp.MustCompile("GCLookupEnable, GlobalCatalog1, GlobalCatalog2, GlobalCatalog3 can not be configured for Extended Schema"), + }, + + { + // error Extended Schema with remote role mapping config + Config: testAccRedfishDirectoryServiceAuthProviderExtendedRemoteRoleConfig(creds), + ExpectError: regexp.MustCompile("RemoteRoleMapping can not be configured for Extended Schema"), + }, + { + // error Extended Schema with groupdomain config + Config: testAccRedfishDirectoryServiceAuthProviderExtendedADGroupDomainConfig(creds), + ExpectError: regexp.MustCompile("Domain can not be configured for Extended Schema"), + }, + { + // error Standard Schema with RacName and RacDomain + Config: testAccRedfishDirectoryServiceAuthProviderStandardSchemaAndRacConfig(creds), + ExpectError: regexp.MustCompile("RacName and RacDomain can not be configured for Standard Schema"), + }, + { + // error Standard Schema without GCLookup config + Config: testAccRedfishDirectoryServiceAuthProviderStandardSchemaNoGCLookUpConfig(creds), + ExpectError: regexp.MustCompile("GCLookupEnable must be configured for Standard Schema"), + }, + { + // error Standard Schema, GCLookup Enabled, no GCRootDomain + Config: testAccRedfishDirectoryServiceAuthProviderStandardSchemaNoGCRootConfig(creds), + ExpectError: regexp.MustCompile("GCRootDomain must be configured for Enabled GCLookupEnable"), + }, + { + // error Standard Schema GCLookup Enabled, with global catalog config + Config: testAccRedfishDirectoryServiceAuthProviderStandardSchemaGlobalCatalogConfig(creds), + ExpectError: regexp.MustCompile("GlobalCatalog can not be configured for Enabled GCLookupEnable"), + }, + { + // error Standard Schema GCLookup Disabled with no Globalcatalog config + Config: testAccRedfishDirectoryServiceAuthProviderStandardSchemaNoGlobalCatalogConfig(creds), + ExpectError: regexp.MustCompile("Invalid GlobalCatalog configuration for Standard Schema"), + }, + { + // error Standard Schema GCLookup Disabled with GcRootDomain config + Config: testAccRedfishDirectoryServiceAuthProviderStandardSchemaGCRootConfig(creds), + ExpectError: regexp.MustCompile("GCRootDomain can not be configured for Disabled GCLookupEnable"), + }, + }, + }) +} + +func TestAccRedfishDirectoryServiceAuthProviderImport(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: `resource "redfish_directory_service_auth_provider" "ds_auth" { + }`, + ResourceName: "redfish_directory_service_auth_provider.ds_auth", + ImportState: true, + ImportStateId: "{\"username\":\"" + creds.Username + "\",\"password\":\"" + creds.Password + "\",\"endpoint\":\"https://" + creds.Endpoint + "\",\"ssl_insecure\":true}", + ExpectError: nil, + }, + }, + }) +} + +func testAccRedfishDirectoryServiceAuthProviderErrorConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + remote_role_mapping = [ + { + local_role = "Administrator", + remote_group = "xxxx" + } + ], + service_addresses = [ + "yulanadhost11.yulan.pie.lab.emc.com" + ], + service_enabled = true + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Enabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com" + } + + ldap = { + directory = { + remote_role_mapping = [ + { + local_role = "Administrator", + remote_group = "cn = idracgroup,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + } + ], + service_addresses = [ + "yulanadhost12.yulan.pie.lab.emc.com" + ], + service_enabled = false + }, + ldap_service = { + search_settings = { + base_distinguished_names = [ + "dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + ], + group_name_attribute = "name", + user_name_attribute = "member" + } + } + } + + ldap_attributes = { + "LDAP.1.GroupAttributeIsDN" = "Enabled" + "LDAP.1.Port" = "636", + "LDAP.1.BindDN" = "cn = adtester,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com", + "LDAP.1.BindPassword" = "", + "LDAP.1.SearchFilter" = "(objectclass = *)" + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderADConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + active_directory = { + directory = { + service_enabled = true + + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderLDAPConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + ldap = { + directory = { + remote_role_mapping = [ + { + local_role = "Administrator", + remote_group = "cn = idracgroup,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + } + ], + service_addresses = [ + "yulanadhost12.yulan.pie.lab.emc.com" + ], + service_enabled = false + }, + ldap_service = { + search_settings = { + base_distinguished_names = [ + "dc = yulan,dc = pie,dc = lab,dc = emc,dc = com" + ], + group_name_attribute = "name", + user_name_attribute = "member" + } + } + } + + ldap_attributes = { + "LDAP.1.GroupAttributeIsDN" = "Enabled" + "LDAP.1.Port" = "636", + "LDAP.1.BindDN" = "cn = adtester,cn = users,dc = yulan,dc = pie,dc = lab,dc = emc,dc = com", + "LDAP.1.BindPassword" = "", + "LDAP.1.SearchFilter" = "(objectclass = *)" + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderAD_UpdateConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = false, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "130", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderEmptyAuth(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + + service_addresses = [ + "yulanadhost11.yulan.pie.lab.emc.com" + ], + service_enabled = true + } + } + + active_directory_attributes = { + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com" + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderInvalidAuth(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + + service_addresses = [ + "yulanadhost11.yulan.pie.lab.emc.com" + ], + service_enabled = true + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "12", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + #"ADGroup.1.Domain" = "yulan.pie.lab.emc.com", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com" + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderADDisableSSOEnable(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + + service_addresses = [ + "yulanadhost11.yulan.pie.lab.emc.com" + ], + service_enabled = false + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Enabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + #"ADGroup.1.Domain" = "yulan.pie.lab.emc.com", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com" + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderADEnSSOEnNoKb(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + + service_addresses = [ + "yulanadhost11.yulan.pie.lab.emc.com" + ], + service_enabled = true + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Enabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com" + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderDClookUpEnServiceAddEmpty(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com" + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderDCLookupByUserDomainConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_addresses = [ + "yulanadhost12.yulan.pie.lab.emc.com" + ], + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Disabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderDCLookupDomainNameConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_addresses = [ + "yulanadhost12.yulan.pie.lab.emc.com" + ], + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Disabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + #"ActiveDirectory.1.DCLookupByUserDomain":"Disabled", + "ActiveDirectory.1.DCLookupDomainName"="test", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderDDCLookupEnableNoServiceAddConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_addresses = [ + "yulanadhost12.yulan.pie.lab.emc.com" + ], + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Disabled", + "ActiveDirectory.1.DCLookupDomainName"="test", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderDCLookupByUserDomainEmptyConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupDomainName"="test", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderDCLookupDomainNameEmptyConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Disabled", + + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderDCLookupEnableDCLookupDomainNameConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + "ActiveDirectory.1.DCLookupDomainName"="test", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderExtendedNoRacConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderExtendedEmptyRacConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "ActiveDirectory.1.RacDomain"= "", + "ActiveDirectory.1.RacName"= "", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderExtendedGCLookUpConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + "ActiveDirectory.1.GCLookupEnable" = "Disabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderExtendedRemoteRoleConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + remote_role_mapping = [ + { + local_role = "Administrator", + remote_group = "xxxx" + } + ], + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderExtendedADGroupDomainConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Extended Schema", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + "ADGroup.1.Domain" = "yulan.pie.lab.emc.com", + + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderStandardSchemaAndRacConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Standard Schema", + "ActiveDirectory.1.RacDomain"= "test", + "ActiveDirectory.1.RacName"= "test", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderStandardSchemaNoGCLookUpConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Standard Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderStandardSchemaNoGCRootConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Standard Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + "ActiveDirectory.1.GCLookupEnable" = "Enabled", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderStandardSchemaGlobalCatalogConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Standard Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + "ActiveDirectory.1.GCLookupEnable" = "Enabled", + "ActiveDirectory.1.GCRootDomain" = "test", + "ActiveDirectory.1.GlobalCatalog1" = "yulanadhost11.yulan.pie.lab.emc.com", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderStandardSchemaNoGlobalCatalogConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Standard Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + "ActiveDirectory.1.GCLookupEnable" = "Disabled", + "ActiveDirectory.1.GlobalCatalog1" = "", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +} + +func testAccRedfishDirectoryServiceAuthProviderStandardSchemaGCRootConfig(testingInfo TestingServerCredentials) string { + return fmt.Sprintf(` + resource "redfish_directory_service_auth_provider" "ds_auth" { + redfish_server { + user = "%s" + password = "%s" + endpoint = "https://%s" + ssl_insecure = true + } + + active_directory = { + directory = { + service_enabled = true, + authentication = { + kerberos_key_tab_file = "" + } + } + } + + active_directory_attributes = { + "ActiveDirectory.1.AuthTimeout"= "120", + "ActiveDirectory.1.CertValidationEnable"= "Enabled", + "ActiveDirectory.1.DCLookupEnable"= "Enabled", + "ActiveDirectory.1.SSOEnable"= "Disabled", + "ActiveDirectory.1.Schema"= "Standard Schema", + "UserDomain.1.Name"= "yulan.pie.lab.emc.com", + "ActiveDirectory.1.DCLookupByUserDomain":"Enabled", + "ActiveDirectory.1.GCLookupEnable" = "Disabled", + "ActiveDirectory.1.GCRootDomain" = "test", + "ActiveDirectory.1.GlobalCatalog1" = "yulanadhost11.yulan.pie.lab.emc.com", + } + } + `, + testingInfo.Username, + testingInfo.Password, + testingInfo.Endpoint, + ) +}