diff --git a/access.go b/access.go index 2225548..39336c4 100644 --- a/access.go +++ b/access.go @@ -161,6 +161,10 @@ func (c *Client) Users(ctx context.Context) (users Users, err error) { return } +func (c *Client) NewUser(ctx context.Context, user *NewUser) (err error) { + return c.Post(ctx, "/access/users", user, nil) +} + func (u *User) Update(ctx context.Context) error { return u.client.Put(ctx, fmt.Sprintf("/access/users/%s", u.UserID), u, nil) } @@ -169,11 +173,46 @@ func (u *User) Delete(ctx context.Context) error { return u.client.Delete(ctx, fmt.Sprintf("/access/users/%s", u.UserID), nil) } +func (u *User) GetAPITokens(ctx context.Context) (tokens Tokens, err error) { + return tokens, u.client.Get(ctx, fmt.Sprintf("/access/users/%s/token", u.UserID), &tokens) +} + +func (u *User) APIToken(ctx context.Context, tokenid string) (token Token, err error) { + return token, u.client.Get(ctx, fmt.Sprintf("/access/users/%s/token/%s", u.UserID, tokenid), &token) +} + +func (u *User) NewAPIToken(ctx context.Context, token Token) (newtoken NewAPIToken, err error) { + return newtoken, u.client.Post(ctx, fmt.Sprintf("/access/users/%s/token/%s", u.UserID, token.TokenID), token, &newtoken) +} + +func (u *User) UpdateAPIToken(ctx context.Context, tokenid string) (token Token, err error) { + return token, u.client.Put(ctx, fmt.Sprintf("/access/users/%s/token/%s", u.UserID, tokenid), token, nil) +} + +func (u *User) DeleteAPIToken(ctx context.Context, tokenid string) error { + return u.client.Delete(ctx, fmt.Sprintf("/access/users/%s/token/%s", u.UserID, tokenid), nil) +} + +func (u *User) GetTFA(ctx context.Context) (tfa TFA, err error) { + return tfa, u.client.Get(ctx, fmt.Sprintf("/access/users/%s/tfa", u.UserID), &tfa) +} + +func (u *User) UnlockTFA(ctx context.Context) error { + return u.client.Delete(ctx, fmt.Sprintf("/access/users/%s/tfa", u.UserID), nil) +} + func (c *Client) Role(ctx context.Context, roleid string) (role Permission, err error) { err = c.Get(ctx, fmt.Sprintf("/access/roles/%s", roleid), &role) return } +func (c *Client) NewRole(ctx context.Context, roleID string, privs string) (err error) { + return c.Post(ctx, "/access/roles", map[string]string{ + "roleid": roleID, + "privs": privs, + }, nil) +} + func (c *Client) Roles(ctx context.Context) (roles Roles, err error) { err = c.Get(ctx, "/access/roles", &roles) if nil == err { diff --git a/access_test.go b/access_test.go index e068be2..e8e6774 100644 --- a/access_test.go +++ b/access_test.go @@ -271,3 +271,139 @@ func TestDomain_Sync(t *testing.T) { domain.Realm = "test" assert.Nil(t, domain.Sync(ctx, DomainSyncOptions{})) } + +func TestNewUser(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + user := NewUser{ + UserID: "test", + } + assert.Nil(t, client.NewUser(ctx, &user)) + +} + +func TestAPITokens(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + + user := &User{ + client: client, + UserID: "test", + } + + apitokens, err := user.GetAPITokens(ctx) + assert.Nil(t, err) + assert.Len(t, apitokens, 2) +} + +func TestAPIToken(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + + User := &User{ + client: client, + UserID: "root@pam", + } + token, err := User.APIToken(ctx, "test") + assert.Nil(t, err) + assert.NotNil(t, token) + +} + +func TestUpdateAPIToken(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + + User := &User{ + client: client, + UserID: "userid", + } + token, err := User.UpdateAPIToken(ctx, "tokenid") + assert.Nil(t, err) + assert.NotNil(t, token) +} + +// func TestNewAPIToken(t *testing.T) { +// mocks.On(mockConfig) +// defer mocks.Off() +// client := mockClient() +// ctx := context.Background() + +// // Users +// user := &User{ +// client: client, +// UserID: "userid", +// } + +// token := &Token{ +// TokenID: "test", +// Comment: "test", +// Expire: 0, +// } + +// newToken, err := user.NewAPIToken(ctx, *token) +// assert.Nil(t, err) +// // Check if newToken.Value is not empty +// assert.NotEmpty(t, newToken.Value) +// // Check if fullTokenid = userid!tokenid +// assert.Equal(t, "userid!test", newToken.FullTokenID) +// } + +func TestDeleteAPIToken(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + + User := &User{ + client: client, + UserID: "root@pam", + } + assert.Nil(t, User.DeleteAPIToken(ctx, "test")) +} + +func TestNewRole(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + + assert.Nil(t, client.NewRole(ctx, "test", "test")) +} + +func TestGetTFA(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + + user := &User{ + client: client, + UserID: "userid", + } + + tfa, err := user.GetTFA(ctx) + assert.Nil(t, err) + assert.Equal(t, "userid", tfa.User) +} + +func TestUnlockTFA(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + + user := &User{ + client: client, + UserID: "userid", + } + assert.Nil(t, user.UnlockTFA(ctx)) +} diff --git a/tests/mocks/pve7x/access.go b/tests/mocks/pve7x/access.go index 8e4f16b..a740967 100644 --- a/tests/mocks/pve7x/access.go +++ b/tests/mocks/pve7x/access.go @@ -869,4 +869,87 @@ func access() { Post("^/access/groups/groupid"). Reply(200). JSON(``) + + gock.New(config.C.URI). + Post("^/access/users"). + Reply(200). + JSON(``) + + gock.New(config.C.URI). + Post("^/access/roles"). + Reply(200). + JSON(``) + + gock.New(config.C.URI). + Get("^/access/users/test/token"). + Reply(200). + JSON(`{ + "data": [ + { + "expire": 100, + "privsep": 0, + "tokenid": "test", + "comment": "comment" + }, + { + "expire": 0, + "privsep": 1, + "tokenid": "test2", + "comment": "comment" + } + ] +}`) + + gock.New(config.C.URI). + Get("^/access/users/root@pam/token/test"). + Reply(200). + JSON(`{ + "data": { + "expire": 0, + "privsep": 0, + "comment": "comment" + } +}`) + + gock.New(config.C.URI). + Post("^/access/users/userid/token/test"). + Reply(200). + JSON(`{ + "data": {"full-tokenid":"userid!test","value":"tokenvalue"} +}`) + + gock.New(config.C.URI). + Delete("^/access/users/root@pam/token/test"). + Reply(200). + JSON(``) + + gock.New(config.C.URI). + Get("^/access/users/userid/tfa"). + Reply(200). + JSON(`{ + "data": { + "realm": "pve", + "types": [ + "oath" + ], + "user": "userid" + } +}`) + + gock.New(config.C.URI). + Delete("^/access/users/userid/tfa"). + Reply(200). + JSON(``) + + gock.New(config.C.URI). + Put("^/access/users/userid/token/tokenid"). + Reply(200). + JSON(` + "data": { + "expire": 0, + "privsep": 0, + "comment": "comment" + } +}`) + } diff --git a/types.go b/types.go index cdd496b..7736c6f 100644 --- a/types.go +++ b/types.go @@ -1181,3 +1181,28 @@ type CustomCertificate struct { Key string `json:"key,omitempty"` // PEM encoded private key Restart bool `json:"restart,omitempty"` // restart pveproxy } + +type NewUser struct { + UserID string `json:"userid"` + Comment string `json:"comment,omitempty"` + Email string `json:"email,omitempty"` + Enable bool `json:"enable,omitempty"` + Expire int `json:"expire,omitempty"` + Firstname string `json:"firstname,omitempty"` + Groups []string `json:"groups,omitempty"` + Keys []string `json:"keys,omitempty"` + Lastname string `json:"lastname,omitempty"` + Password string `json:"password,omitempty"` +} + +type TFA struct { + Realm string `json:"realm,omitempty"` + Types []string `json:"types,omitempty"` + User string `json:"user,omitempty"` +} + +type NewAPIToken struct { + FullTokenID string `json:"full-tokenid,omitempty"` + Info interface{} `json:"info,omitempty"` + Value string `json:"value,omitempty"` +}