Fix querying and authentication for Zabbix v7.0+ (#1931)

* Fix querying for Zabbix v7.2+

* Update check from 7.2 to 7.0

* Fix also select acknowledges key

* Remove unsused methods

* release commit 4.6.0

---------

Co-authored-by: yesoreyeram <153843+yesoreyeram@users.noreply.github.com>
This commit is contained in:
Ivana Huckova
2024-12-17 02:02:43 +01:00
committed by GitHub
parent 0225320a62
commit dbcc008489
15 changed files with 1226 additions and 84 deletions

View File

@@ -494,7 +494,13 @@ func (ds *Zabbix) GetAllGroups(ctx context.Context) ([]Group, error) {
params := ZabbixAPIParams{
"output": []string{"name", "groupid"},
"sortfield": "name",
"real_hosts": true,
}
// Zabbix v7.0 and later removed `real_hosts` parameter and replaced it with `with_hosts`
if ds.version < 70 {
params["real_hosts"] = true
} else {
params["with_hosts"] = true
}
result, err := ds.Request(ctx, &ZabbixAPIRequest{Method: "hostgroup.get", Params: params})

View File

@@ -89,10 +89,10 @@ func (zabbix *Zabbix) request(ctx context.Context, method string, params ZabbixA
// Skip auth for methods that are not required it
if method == "apiinfo.version" {
return zabbix.api.RequestUnauthenticated(ctx, method, params)
return zabbix.api.RequestUnauthenticated(ctx, method, params, zabbix.version)
}
result, err := zabbix.api.Request(ctx, method, params)
result, err := zabbix.api.Request(ctx, method, params, zabbix.version)
notAuthorized := isNotAuthorized(err)
isTokenAuth := zabbix.settings.AuthType == settings.AuthTypeToken
if err == zabbixapi.ErrNotAuthenticated || (notAuthorized && !isTokenAuth) {
@@ -141,7 +141,7 @@ func (zabbix *Zabbix) Authenticate(ctx context.Context) error {
zabbixPassword = jsonData.Get("password").MustString()
}
err = zabbix.api.Authenticate(ctx, zabbixLogin, zabbixPassword)
err = zabbix.api.Authenticate(ctx, zabbixLogin, zabbixPassword, zabbix.version)
if err != nil {
zabbix.logger.Error("Zabbix authentication error", "error", err)
return err

View File

@@ -74,20 +74,20 @@ func (api *ZabbixAPI) SetAuth(auth string) {
}
// Request performs API request
func (api *ZabbixAPI) Request(ctx context.Context, method string, params ZabbixAPIParams) (*simplejson.Json, error) {
func (api *ZabbixAPI) Request(ctx context.Context, method string, params ZabbixAPIParams, version int) (*simplejson.Json, error) {
if api.auth == "" {
return nil, ErrNotAuthenticated
}
return api.request(ctx, method, params, api.auth)
return api.request(ctx, method, params, api.auth, version)
}
// Request performs API request without authentication token
func (api *ZabbixAPI) RequestUnauthenticated(ctx context.Context, method string, params ZabbixAPIParams) (*simplejson.Json, error) {
return api.request(ctx, method, params, "")
func (api *ZabbixAPI) RequestUnauthenticated(ctx context.Context, method string, params ZabbixAPIParams, version int) (*simplejson.Json, error) {
return api.request(ctx, method, params, "", version)
}
func (api *ZabbixAPI) request(ctx context.Context, method string, params ZabbixAPIParams, auth string) (*simplejson.Json, error) {
func (api *ZabbixAPI) request(ctx context.Context, method string, params ZabbixAPIParams, auth string, version int) (*simplejson.Json, error) {
apiRequest := map[string]interface{}{
"jsonrpc": "2.0",
"id": 2,
@@ -95,7 +95,9 @@ func (api *ZabbixAPI) request(ctx context.Context, method string, params ZabbixA
"params": params,
}
if auth != "" {
// Zabbix v7.2 and later deprecated `auth` parameter and replaced it with using Auth header
// `auth` parameter throws an error in new versions so we need to add it only for older versions
if auth != "" && version < 70 {
apiRequest["auth"] = auth
}
@@ -111,6 +113,9 @@ func (api *ZabbixAPI) request(ctx context.Context, method string, params ZabbixA
metrics.ZabbixAPIQueryTotal.WithLabelValues(method).Inc()
if auth != "" {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", auth))
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Grafana/grafana-zabbix")
@@ -123,13 +128,13 @@ func (api *ZabbixAPI) request(ctx context.Context, method string, params ZabbixA
}
// Login performs API authentication and returns authentication token.
func (api *ZabbixAPI) Login(ctx context.Context, username string, password string) (string, error) {
func (api *ZabbixAPI) Login(ctx context.Context, username string, password string, version int) (string, error) {
params := ZabbixAPIParams{
"username": username,
"password": password,
}
auth, err := api.request(ctx, "user.login", params, "")
auth, err := api.request(ctx, "user.login", params, "", version)
if err != nil {
return "", err
}
@@ -138,13 +143,13 @@ func (api *ZabbixAPI) Login(ctx context.Context, username string, password strin
}
// Login method for Zabbix prior to 5.4
func (api *ZabbixAPI) LoginDeprecated(ctx context.Context, username string, password string) (string, error) {
func (api *ZabbixAPI) LoginDeprecated(ctx context.Context, username string, password string, version int) (string, error) {
params := ZabbixAPIParams{
"user": username,
"password": password,
}
auth, err := api.request(ctx, "user.login", params, "")
auth, err := api.request(ctx, "user.login", params, "", version)
if err != nil {
return "", err
}
@@ -153,11 +158,11 @@ func (api *ZabbixAPI) LoginDeprecated(ctx context.Context, username string, pass
}
// Authenticate performs API authentication and sets authentication token.
func (api *ZabbixAPI) Authenticate(ctx context.Context, username string, password string) error {
auth, err := api.Login(ctx, username, password)
func (api *ZabbixAPI) Authenticate(ctx context.Context, username string, password string, version int) error {
auth, err := api.Login(ctx, username, password, version)
if isDeprecatedUserParamError(err) {
api.logger.Debug("user.login method error, switching to deprecated user parameter", "error", err)
auth, err = api.LoginDeprecated(ctx, username, password)
auth, err = api.LoginDeprecated(ctx, username, password, version)
if err != nil {
return err
}

View File

@@ -7,9 +7,12 @@ import (
"github.com/stretchr/testify/assert"
)
var version = 65
func TestZabbixAPIUnauthenticatedQuery(t *testing.T) {
zabbixApi, _ := MockZabbixAPI(`{"result":"sampleResult"}`, 200)
resp, err := zabbixApi.RequestUnauthenticated(context.Background(), "test.get", map[string]interface{}{})
resp, err := zabbixApi.RequestUnauthenticated(context.Background(), "test.get", map[string]interface{}{}, version)
assert.Equal(t, "sampleResult", resp.MustString())
assert.Nil(t, err)
@@ -17,7 +20,7 @@ func TestZabbixAPIUnauthenticatedQuery(t *testing.T) {
func TestLogin(t *testing.T) {
zabbixApi, _ := MockZabbixAPI(`{"result":"secretauth"}`, 200)
err := zabbixApi.Authenticate(context.Background(), "user", "password")
err := zabbixApi.Authenticate(context.Background(), "user", "password", version)
assert.Nil(t, err)
assert.Equal(t, "secretauth", zabbixApi.auth)
@@ -31,6 +34,7 @@ func TestZabbixAPI(t *testing.T) {
mockApiResponseCode int
expectedResult string
expectedError error
version int
}{
{
name: "Simple request",
@@ -54,7 +58,7 @@ func TestZabbixAPI(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
zabbixApi, _ := MockZabbixAPI(tt.mockApiResponse, tt.mockApiResponseCode)
zabbixApi.auth = tt.auth
resp, err := zabbixApi.Request(context.Background(), "test.get", map[string]interface{}{})
resp, err := zabbixApi.Request(context.Background(), "test.get", map[string]interface{}{}, version)
if tt.expectedError != nil {
assert.Equal(t, err, tt.expectedError)