From 7257532ea8223a4e595711b51cedb4156b1cfd30 Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Wed, 16 Oct 2024 17:01:39 -0500 Subject: [PATCH] Add Supermicro OEM AccountService (#374) This adds an OEM version of the AccountService to expose a few OEM-specific properties. Signed-off-by: Sean McGinnis --- oem/smc/accountservice.go | 64 ++++++++++++++++++ oem/smc/accountservice_test.go | 114 +++++++++++++++++++++++++++++++++ redfish/accountservice.go | 8 +-- 3 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 oem/smc/accountservice.go create mode 100644 oem/smc/accountservice_test.go diff --git a/oem/smc/accountservice.go b/oem/smc/accountservice.go new file mode 100644 index 00000000..9d7acf2d --- /dev/null +++ b/oem/smc/accountservice.go @@ -0,0 +1,64 @@ +// +// SPDX-License-Identifier: BSD-3-Clause +// + +package smc + +import ( + "encoding/json" + + "github.com/stmcginnis/gofish/redfish" +) + +// AccountService is a Supermicro OEM instance of an AccountService. +type AccountService struct { + redfish.AccountService + SMCLDAP struct { + StartTLSEnabled bool + } + SMCActiveDirectory struct { + DNSLookupEnable bool + Prefix string + Port int + UserDomainNames []string + DynamicServerAddresses []string + } +} + +// FromAccountService converts a standard AccountService object to the OEM implementation. +func FromAccountService(accountService *redfish.AccountService) (*AccountService, error) { + as := AccountService{ + AccountService: *accountService, + } + + var t struct { + Oem struct { + Supermicro struct { + LDAP struct { + StartTLSEnabled bool + } `json:"LDAP"` + ActiveDirectory struct { + DNSLookupEnable bool + Prefix string + Port int + UserDomainNames []string + DynamicServerAddresses []string + } `json:"ActiveDirectory"` + } `json:"Supermicro"` + } `json:"Oem"` + } + + err := json.Unmarshal(accountService.RawData, &t) + if err != nil { + return nil, err + } + + as.SMCLDAP.StartTLSEnabled = t.Oem.Supermicro.LDAP.StartTLSEnabled + as.SMCActiveDirectory.DNSLookupEnable = t.Oem.Supermicro.ActiveDirectory.DNSLookupEnable + as.SMCActiveDirectory.Prefix = t.Oem.Supermicro.ActiveDirectory.Prefix + as.SMCActiveDirectory.Port = t.Oem.Supermicro.ActiveDirectory.Port + as.SMCActiveDirectory.UserDomainNames = t.Oem.Supermicro.ActiveDirectory.UserDomainNames + as.SMCActiveDirectory.DynamicServerAddresses = t.Oem.Supermicro.ActiveDirectory.DynamicServerAddresses + + return &as, nil +} diff --git a/oem/smc/accountservice_test.go b/oem/smc/accountservice_test.go new file mode 100644 index 00000000..6c2c4d7a --- /dev/null +++ b/oem/smc/accountservice_test.go @@ -0,0 +1,114 @@ +// +// SPDX-License-Identifier: BSD-3-Clause +// + +package smc + +import ( + "encoding/json" + "testing" + + "github.com/stmcginnis/gofish/redfish" +) + +var accountServiceBody = `{ + "@odata.type": "#AccountService.v1_7_2.AccountService", + "@odata.id": "/redfish/v1/AccountService", + "Id": "AccountService", + "Name": "Account Service", + "Description": "Account Service", + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "ServiceEnabled": true, + "MinPasswordLength": 8, + "MaxPasswordLength": 20, + "AuthFailureLoggingThreshold": 3, + "AccountLockoutThreshold": 3, + "AccountLockoutDuration": 30, + "AccountLockoutCounterResetAfter": 30, + "Accounts": { + "@odata.id": "/redfish/v1/AccountService/Accounts" + }, + "Roles": { + "@odata.id": "/redfish/v1/AccountService/Roles" + }, + "LDAP": { + "AccountProviderType": "LDAPService", + "ServiceEnabled": false, + "ServiceAddresses": [], + "Authentication": { + "AuthenticationType": "UsernameAndPassword", + "Username": "", + "Password": null, + "Oem": {} + }, + "PasswordSet": false, + "RemoteRoleMapping": [], + "LDAPService": { + "SearchSettings": { + "BaseDistinguishedNames": [] + }, + "Oem": {} + } + }, + "ActiveDirectory": { + "AccountProviderType": "ActiveDirectoryService", + "ServiceEnabled": false, + "ServiceAddresses": [], + "Authentication": { + "AuthenticationType": "UsernameAndPassword", + "Username": "", + "Password": null, + "Oem": {} + }, + "PasswordSet": false, + "RemoteRoleMapping": [] + }, + "Oem": { + "Supermicro": { + "@odata.type": "#SmcAccountServiceExtensions.v1_0_1.AccountService", + "LDAP": { + "StartTLSEnabled": true + }, + "ActiveDirectory": { + "DNSLookupEnable": true, + "Prefix": "ldap", + "Port": 389, + "UserDomainNames": ["example.com"], + "DynamicServerAddresses": [] + } + } + }, + "@odata.etag": "\"01dc844f1c2c3fae22b77263291f161b\"" +}` + +// TestSmcAccountServiceOem tests the parsing of the AccountService oem field +func TestSmcAccountServiceOem(t *testing.T) { + drive := &redfish.AccountService{} + if err := json.Unmarshal([]byte(accountServiceBody), drive); err != nil { + t.Fatalf("error decoding json: %v", err) + } + + accountService, err := FromAccountService(drive) + if err != nil { + t.Fatalf("error getting oem info from drive: %v", err) + } + + if accountService.ID != "AccountService" { + t.Errorf("unexpected ID: %s", accountService.ID) + } + + if !accountService.SMCLDAP.StartTLSEnabled { + t.Errorf("unexpected StartTLSEnabled state: %t", accountService.SMCLDAP.StartTLSEnabled) + } + + if !accountService.SMCActiveDirectory.DNSLookupEnable || + accountService.SMCActiveDirectory.Prefix != "ldap" || + accountService.SMCActiveDirectory.Port != 389 || + len(accountService.SMCActiveDirectory.UserDomainNames) != 1 || + accountService.SMCActiveDirectory.UserDomainNames[0] != "example.com" { + t.Errorf("unexpected ActiveDirectory settings: %+v", accountService.SMCActiveDirectory) + } +} diff --git a/redfish/accountservice.go b/redfish/accountservice.go index 5be51520..48e2cbaf 100644 --- a/redfish/accountservice.go +++ b/redfish/accountservice.go @@ -388,8 +388,8 @@ type AccountService struct { // populated by default. This entity shall not be present in the additional external account providers resource // collection. TACACSplus ExternalAccountProvider - // rawData holds the original serialized JSON so we can compare updates. - rawData []byte + // RawData holds the original serialized JSON so we can compare updates. + RawData []byte } // UnmarshalJSON unmarshals an AccountService object from the raw JSON. @@ -427,7 +427,7 @@ func (accountservice *AccountService) UnmarshalJSON(b []byte) error { accountservice.privilegeMap = t.PrivilegeMap.String() // This is a read/write object, so we need to save the raw object data for later - accountservice.rawData = b + accountservice.RawData = b return nil } @@ -450,7 +450,7 @@ func (accountservice *AccountService) Update() error { // Get a representation of the object's original state so we can find what // to update. original := new(AccountService) - err := original.UnmarshalJSON(accountservice.rawData) + err := original.UnmarshalJSON(accountservice.RawData) if err != nil { return err }