Skip to content

Commit b2555c0

Browse files
authored
Merge pull request #242 from bmc-toolbox/redfish-useraccounts
Redfish useraccounts
2 parents 7bc798f + 6a51f16 commit b2555c0

File tree

12 files changed

+715
-18
lines changed

12 files changed

+715
-18
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ With approval from [Booking.com](http://www.booking.com), the code and
8484
specification were generalized and published as Open Source on github, for
8585
which the authors would like to express their gratitude.
8686

87+
bmclib interfaces with Redfish with https://github.com/stmcginnis/gofish
88+
8789
#### Authors
8890
- Juliano Martinez
89-
- Joel Rebello
91+
- Joel Rebello
9092
- Guilherme M. Schroeder
9193
- Mariano Guezuraga

errors/errors.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,27 @@ var (
5050

5151
// ErrDeviceNotMatched is the error returned when the device was not a type it was probed for
5252
ErrDeviceNotMatched = errors.New("the vendor device did not match the probe")
53+
54+
// ErrRetrievingUserAccounts is returned when bmclib is unable to retrieve user accounts from the BMC
55+
ErrRetrievingUserAccounts = errors.New("error retrieving user accounts")
56+
57+
// ErrInvalidUserRole is returned when the given user account role is not valid
58+
ErrInvalidUserRole = errors.New("invalid user account role")
59+
60+
// ErrUserParamsRequired is returned when all the required user parameters are not provided - username, password, role
61+
ErrUserParamsRequired = errors.New("username, password and role are required parameters")
62+
63+
// ErrUserAccountExists is returned when a user account with the username is already present
64+
ErrUserAccountExists = errors.New("user account already exists")
65+
66+
// ErrNoUserSlotsAvailable is returned when there are no user account slots available
67+
ErrNoUserSlotsAvailable = errors.New("no user account slots available")
68+
69+
// ErrUserAccountNotFound is returned when the user account is not present
70+
ErrUserAccountNotFound = errors.New("given user account does not exist")
71+
72+
// ErrUserAccountUpdate is returned when the user account failed to be updated
73+
ErrUserAccountUpdate = errors.New("user account attributes could not be updated")
5374
)
5475

5576
type ErrUnsupportedHardware struct {

examples/v1/users/user.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"log"
6+
"time"
7+
8+
"github.com/bmc-toolbox/bmclib"
9+
"github.com/bombsimon/logrusr"
10+
"github.com/sirupsen/logrus"
11+
)
12+
13+
func main() {
14+
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
15+
defer cancel()
16+
17+
// set BMC parameters here
18+
host := ""
19+
port := ""
20+
user := ""
21+
pass := ""
22+
23+
l := logrus.New()
24+
l.Level = logrus.DebugLevel
25+
logger := logrusr.NewLogger(l)
26+
27+
if host == "" || user == "" || pass == "" {
28+
log.Fatal("required host/user/pass parameters not defined")
29+
}
30+
31+
cl := bmclib.NewClient(host, port, user, pass, bmclib.WithLogger(logger))
32+
33+
cl.Registry.Drivers = cl.Registry.Using("redfish")
34+
// cl.Registry.Drivers = cl.Registry.Using("vendorapi")
35+
36+
err := cl.Open(ctx)
37+
if err != nil {
38+
log.Fatal(err, "bmc login failed")
39+
}
40+
41+
defer cl.Close(ctx)
42+
43+
_, err = cl.CreateUser(ctx, "foobar", "sekurity101", "Administrator")
44+
if err != nil {
45+
l.Error(err)
46+
}
47+
48+
}

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ require (
2626
github.com/spf13/jwalterweatherman v1.1.0 // indirect
2727
github.com/spf13/pflag v1.0.5 // indirect
2828
github.com/spf13/viper v1.7.1
29-
github.com/stmcginnis/gofish v0.8.0
29+
github.com/stmcginnis/gofish v0.12.0
30+
github.com/stretchr/testify v1.7.0
3031
go.uber.org/multierr v1.6.0 // indirect
3132
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670
3233
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4

go.sum

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,15 +250,16 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
250250
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
251251
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
252252
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
253-
github.com/stmcginnis/gofish v0.8.0 h1:lGMKmyHQr8rG6SHKL9SU8BuA4bV/r3Pj/8J+DkuT0gY=
254-
github.com/stmcginnis/gofish v0.8.0/go.mod h1:BGtQsY16q48M2K6KDAs38QXtNoHrkXaY/WZ/mmyMgNc=
253+
github.com/stmcginnis/gofish v0.12.0 h1:6UbNePjA++XkHtCKKLr7envKENxljJ1YyD8f4vS3Zeo=
254+
github.com/stmcginnis/gofish v0.12.0/go.mod h1:BGtQsY16q48M2K6KDAs38QXtNoHrkXaY/WZ/mmyMgNc=
255255
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
256256
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
257257
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
258258
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
259259
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
260-
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
261260
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
261+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
262+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
262263
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
263264
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
264265
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=

internal/utils.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,12 @@ func ValidateUserConfig(cfgUsers []*cfgresources.User) (err error) {
4343

4444
return nil
4545
}
46+
47+
func StringInSlice(str string, sl []string) bool {
48+
for _, s := range sl {
49+
if str == s {
50+
return true
51+
}
52+
}
53+
return false
54+
}

providers/asrockrack/helpers.go

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,49 @@ type biosUpdateAction struct {
5959
Action int `json:"action"`
6060
}
6161

62+
func (a *ASRockRack) listUsers() ([]*UserAccount, error) {
63+
endpoint := "api/settings/users"
64+
65+
resp, statusCode, err := a.queryHTTPS(endpoint, "GET", nil, nil, 0)
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
if statusCode != http.StatusOK {
71+
return nil, fmt.Errorf("non 200 response: %d", statusCode)
72+
}
73+
74+
accounts := []*UserAccount{}
75+
76+
err = json.Unmarshal(resp, &accounts)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
return accounts, nil
82+
}
83+
84+
func (a *ASRockRack) createUpdateUser(account *UserAccount) error {
85+
endpoint := "api/settings/users/" + fmt.Sprintf("%d", account.ID)
86+
87+
payload, err := json.Marshal(account)
88+
if err != nil {
89+
return err
90+
}
91+
92+
headers := map[string]string{"Content-Type": "application/json"}
93+
_, statusCode, err := a.queryHTTPS(endpoint, "PUT", bytes.NewReader(payload), headers, 0)
94+
if err != nil {
95+
return err
96+
}
97+
98+
if statusCode != http.StatusOK {
99+
return fmt.Errorf("non 200 response: %d", statusCode)
100+
}
101+
102+
return nil
103+
}
104+
62105
// 1 Set BMC to flash mode and prepare flash area
63106
// at this point all logged in sessions are terminated
64107
// and no logins are permitted
@@ -70,7 +113,7 @@ func (a *ASRockRack) setFlashMode() error {
70113
return err
71114
}
72115

73-
if statusCode != 200 {
116+
if statusCode != http.StatusOK {
74117
return fmt.Errorf("non 200 response: %d", statusCode)
75118
}
76119

@@ -132,7 +175,7 @@ func (a *ASRockRack) uploadFirmware(endpoint string, fwReader io.Reader, fileSiz
132175
return err
133176
}
134177

135-
if statusCode != 200 {
178+
if statusCode != http.StatusOK {
136179
return fmt.Errorf("non 200 response: %d", statusCode)
137180
}
138181

@@ -148,7 +191,7 @@ func (a *ASRockRack) verifyUploadedFirmware() error {
148191
return err
149192
}
150193

151-
if statusCode != 200 {
194+
if statusCode != http.StatusOK {
152195
return fmt.Errorf("non 200 response: %d", statusCode)
153196
}
154197

@@ -172,7 +215,7 @@ func (a *ASRockRack) upgradeBMC() error {
172215
return err
173216
}
174217

175-
if statusCode != 200 {
218+
if statusCode != http.StatusOK {
176219
return fmt.Errorf("non 200 response: %d", statusCode)
177220
}
178221

@@ -188,7 +231,7 @@ func (a *ASRockRack) reset() error {
188231
return err
189232
}
190233

191-
if statusCode != 200 {
234+
if statusCode != http.StatusOK {
192235
return fmt.Errorf("non 200 response: %d", statusCode)
193236
}
194237

@@ -202,7 +245,7 @@ func (a *ASRockRack) flashProgress(endpoint string) (*upgradeProgress, error) {
202245
return nil, err
203246
}
204247

205-
if statusCode != 200 {
248+
if statusCode != http.StatusOK {
206249
return nil, fmt.Errorf("non 200 response: %d", statusCode)
207250
}
208251

@@ -224,7 +267,7 @@ func (a *ASRockRack) firmwareInfo() (*firmwareInfo, error) {
224267
return nil, err
225268
}
226269

227-
if statusCode != 200 {
270+
if statusCode != http.StatusOK {
228271
return nil, fmt.Errorf("non 200 response: %d", statusCode)
229272
}
230273

@@ -255,7 +298,7 @@ func (a *ASRockRack) biosUpgradeConfiguration() error {
255298
return err
256299
}
257300

258-
if statusCode != 200 {
301+
if statusCode != http.StatusOK {
259302
return fmt.Errorf("non 200 response: %d", statusCode)
260303
}
261304

@@ -285,7 +328,7 @@ func (a *ASRockRack) biosUpgrade() error {
285328
return err
286329
}
287330

288-
if statusCode != 200 {
331+
if statusCode != http.StatusOK {
289332
return fmt.Errorf("non 200 response: %d", statusCode)
290333
}
291334

@@ -343,7 +386,7 @@ func (a *ASRockRack) httpsLogout() error {
343386
return fmt.Errorf("Error logging out: " + err.Error())
344387
}
345388

346-
if statusCode != 200 {
389+
if statusCode != http.StatusOK {
347390
return fmt.Errorf("non 200 response at https logout: %d", statusCode)
348391
}
349392

providers/asrockrack/mock_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package asrockrack
33
import (
44
"bytes"
55
"encoding/json"
6-
"fmt"
76
"io/ioutil"
87
"log"
98
"net/http"
@@ -25,6 +24,9 @@ var (
2524
fwUploadResponse = []byte(`{"cc": 0}`)
2625
fwVerificationResponse = []byte(`[ { "id": 1, "current_image_name": "ast2500e", "current_image_version1": "0.01.00", "current_image_version2": "", "new_image_version": "0.03.00", "section_status": 0, "verification_status": 5 } ]`)
2726
fwUpgradeProgress = []byte(`{ "id": 1, "action": "Flashing...", "progress": "__PERCENT__% done ", "state": __STATE__ }`)
27+
usersPayload = []byte(`[ { "id": 1, "name": "anonymous", "access": 0, "kvm": 1, "vmedia": 1, "snmp": 0, "prev_snmp": 0, "network_privilege": "administrator", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "none", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "ami_format", "ssh_key": "Not Available", "creation_time": 4802 }, { "id": 2, "name": "admin", "access": 1, "kvm": 1, "vmedia": 1, "snmp": 0, "prev_snmp": 0, "network_privilege": "administrator", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "none", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "ami_format", "ssh_key": "Not Available", "creation_time": 188 }, { "id": 3, "name": "foo", "access": 1, "kvm": 1, "vmedia": 1, "snmp": 0, "prev_snmp": 0, "network_privilege": "administrator", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "none", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "ami_format", "ssh_key": "Not Available", "creation_time": 4802 }, { "id": 4, "name": "", "access": 0, "kvm": 0, "vmedia": 0, "snmp": 0, "prev_snmp": 0, "network_privilege": "", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "", "ssh_key": "Not Available", "creation_time": 0 }, { "id": 5, "name": "", "access": 0, "kvm": 0, "vmedia": 0, "snmp": 0, "prev_snmp": 0, "network_privilege": "", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "", "ssh_key": "Not Available", "creation_time": 0 }, { "id": 6, "name": "", "access": 0, "kvm": 0, "vmedia": 0, "snmp": 0, "prev_snmp": 0, "network_privilege": "", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "", "ssh_key": "Not Available", "creation_time": 0 }, { "id": 7, "name": "", "access": 0, "kvm": 0, "vmedia": 0, "snmp": 0, "prev_snmp": 0, "network_privilege": "", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "", "ssh_key": "Not Available", "creation_time": 0 }, { "id": 8, "name": "", "access": 0, "kvm": 0, "vmedia": 0, "snmp": 0, "prev_snmp": 0, "network_privilege": "", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "", "ssh_key": "Not Available", "creation_time": 0 }, { "id": 9, "name": "", "access": 0, "kvm": 0, "vmedia": 0, "snmp": 0, "prev_snmp": 0, "network_privilege": "", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "", "ssh_key": "Not Available", "creation_time": 0 }, { "id": 10, "name": "", "access": 0, "kvm": 0, "vmedia": 0, "snmp": 0, "prev_snmp": 0, "network_privilege": "", "fixed_user_count": 2, "snmp_access": "", "OEMProprietary_level_Privilege": 1, "privilege_limit_serial": "", "snmp_authentication_protocol": "", "snmp_privacy_protocol": "", "email_id": "", "email_format": "", "ssh_key": "Not Available", "creation_time": 0 } ]`)
28+
// TODO: implement under rw mutex
29+
httpRequestTestVar *http.Request
2830
)
2931

3032
// setup test BMC
@@ -80,6 +82,9 @@ func mockASRockBMC() *httptest.Server {
8082
handler.HandleFunc("/api/maintenance/reset", bmcFirmwareUpgrade)
8183
handler.HandleFunc("/api/asrr/maintenance/BIOS/firmware", biosFirmwareUpgrade)
8284

85+
// user accounts endpoints
86+
handler.HandleFunc("/api/settings/users", userAccountList)
87+
handler.HandleFunc("/api/settings/users/3", userAccountList)
8388
return httptest.NewTLSServer(handler)
8489
}
8590

@@ -90,8 +95,20 @@ func index(w http.ResponseWriter, r *http.Request) {
9095
}
9196
}
9297

98+
func userAccountList(w http.ResponseWriter, r *http.Request) {
99+
switch r.Method {
100+
case "GET":
101+
if os.Getenv("TEST_FAIL_QUERY") != "" {
102+
w.WriteHeader(http.StatusInternalServerError)
103+
} else {
104+
_, _ = w.Write(usersPayload)
105+
}
106+
case "PUT":
107+
httpRequestTestVar = r
108+
}
109+
}
110+
93111
func biosFirmwareUpgrade(w http.ResponseWriter, r *http.Request) {
94-
fmt.Printf("%s -> %s\n", r.Method, r.RequestURI)
95112
switch r.Method {
96113
case "POST":
97114
switch r.RequestURI {
@@ -112,7 +129,6 @@ func biosFirmwareUpgrade(w http.ResponseWriter, r *http.Request) {
112129
}
113130

114131
func bmcFirmwareUpgrade(w http.ResponseWriter, r *http.Request) {
115-
fmt.Printf("%s -> %s\n", r.Method, r.RequestURI)
116132
switch r.Method {
117133
case "GET":
118134
switch r.RequestURI {

0 commit comments

Comments
 (0)