Auth: Able to use API tokens for authentication (#1662)

* Auth: Able to use API tokens for authentication

* Update change log
This commit is contained in:
Alexander Zobnin
2023-07-26 18:23:44 +03:00
committed by GitHub
parent 8205f7aaf8
commit ac976945a5
9 changed files with 174 additions and 55 deletions

View File

@@ -3,6 +3,7 @@ package datasource
import (
"github.com/alexanderzobnin/grafana-zabbix/pkg/settings"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
)
@@ -13,7 +14,7 @@ var basicDatasourceInfo = &backend.DataSourceInstanceSettings{
ID: 1,
Name: "TestDatasource",
URL: "http://zabbix.org/zabbix",
JSONData: []byte(`{"username":"username", "password":"password", "cacheTTL":"10m"}`),
JSONData: []byte(`{"username":"username", "password":"password", "cacheTTL":"10m", "authType":"token"}`),
}
func mockZabbixQuery(method string, params zabbix.ZabbixAPIParams) *zabbix.ZabbixAPIRequest {

View File

@@ -2,8 +2,14 @@ package settings
import "time"
const (
AuthTypeUserLogin = "userLogin"
AuthTypeToken = "token"
)
// ZabbixDatasourceSettingsDTO model
type ZabbixDatasourceSettingsDTO struct {
AuthType string `json:"authType"`
Trends bool `json:"trends"`
TrendsFrom string `json:"trendsFrom"`
TrendsRange string `json:"trendsRange"`
@@ -16,6 +22,7 @@ type ZabbixDatasourceSettingsDTO struct {
// ZabbixDatasourceSettings model
type ZabbixDatasourceSettings struct {
AuthType string
Trends bool
TrendsFrom time.Duration
TrendsRange time.Duration

View File

@@ -3,10 +3,12 @@ package settings
import (
"encoding/json"
"errors"
"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"strconv"
"time"
"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
"github.com/grafana/grafana-plugin-sdk-go/backend"
)
func ReadZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings) (*ZabbixDatasourceSettings, error) {
@@ -17,6 +19,10 @@ func ReadZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings)
return nil, err
}
if zabbixSettingsDTO.AuthType == "" {
zabbixSettingsDTO.AuthType = AuthTypeUserLogin
}
if zabbixSettingsDTO.TrendsFrom == "" {
zabbixSettingsDTO.TrendsFrom = "7d"
}
@@ -65,6 +71,7 @@ func ReadZabbixSettings(dsInstanceSettings *backend.DataSourceInstanceSettings)
}
zabbixSettings := &ZabbixDatasourceSettings{
AuthType: zabbixSettingsDTO.AuthType,
Trends: zabbixSettingsDTO.Trends,
TrendsFrom: trendsFrom,
TrendsRange: trendsRange,

View File

@@ -2,6 +2,7 @@ package zabbix
import (
"context"
"errors"
"strings"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/alexanderzobnin/grafana-zabbix/pkg/settings"
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"
"github.com/bitly/go-simplejson"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
)
@@ -16,11 +18,12 @@ import (
// Zabbix is a wrapper for Zabbix API. It wraps Zabbix API queries and performs authentication, adds caching,
// deduplication and other performance optimizations.
type Zabbix struct {
api *zabbixapi.ZabbixAPI
dsInfo *backend.DataSourceInstanceSettings
cache *ZabbixCache
version int
logger log.Logger
api *zabbixapi.ZabbixAPI
dsInfo *backend.DataSourceInstanceSettings
settings *settings.ZabbixDatasourceSettings
cache *ZabbixCache
version int
logger log.Logger
}
// New returns new instance of Zabbix client.
@@ -29,10 +32,11 @@ func New(dsInfo *backend.DataSourceInstanceSettings, zabbixSettings *settings.Za
zabbixCache := NewZabbixCache(zabbixSettings.CacheTTL, 10*time.Minute)
return &Zabbix{
api: zabbixAPI,
dsInfo: dsInfo,
cache: zabbixCache,
logger: logger,
api: zabbixAPI,
dsInfo: dsInfo,
settings: zabbixSettings,
cache: zabbixCache,
logger: logger,
}, nil
}
@@ -90,11 +94,12 @@ func (zabbix *Zabbix) request(ctx context.Context, method string, params ZabbixA
result, err := zabbix.api.Request(ctx, method, params)
notAuthorized := isNotAuthorized(err)
if err == zabbixapi.ErrNotAuthenticated || notAuthorized {
isTokenAuth := zabbix.settings.AuthType == settings.AuthTypeToken
if err == zabbixapi.ErrNotAuthenticated || (notAuthorized && !isTokenAuth) {
if notAuthorized {
zabbix.logger.Debug("Authentication token expired, performing re-login")
}
err = zabbix.Login(ctx)
err = zabbix.Authenticate(ctx)
if err != nil {
return nil, err
}
@@ -106,12 +111,27 @@ func (zabbix *Zabbix) request(ctx context.Context, method string, params ZabbixA
return result, err
}
func (zabbix *Zabbix) Login(ctx context.Context) error {
func (zabbix *Zabbix) Authenticate(ctx context.Context) error {
jsonData, err := simplejson.NewJson(zabbix.dsInfo.JSONData)
if err != nil {
return err
}
authType := zabbix.settings.AuthType
if authType == settings.AuthTypeToken {
token, exists := zabbix.dsInfo.DecryptedSecureJSONData["apiToken"]
if !exists {
return errors.New("cannot find Zabbix API token")
}
err = zabbix.api.AuthenticateWithToken(ctx, token)
if err != nil {
zabbix.logger.Error("Zabbix authentication error", "error", err)
return err
}
zabbix.logger.Debug("Using API token for authentication")
return nil
}
zabbixLogin := jsonData.Get("username").MustString()
var zabbixPassword string
if securePassword, exists := zabbix.dsInfo.DecryptedSecureJSONData["password"]; exists {

View File

@@ -4,8 +4,9 @@ import (
"context"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/assert"
"github.com/grafana/grafana-plugin-sdk-go/backend"
)
var basicDatasourceInfo = &backend.DataSourceInstanceSettings{
@@ -19,7 +20,7 @@ var emptyParams = map[string]interface{}{}
func TestLogin(t *testing.T) {
zabbixClient, _ := MockZabbixClient(basicDatasourceInfo, `{"result":"secretauth"}`, 200)
err := zabbixClient.Login(context.Background())
err := zabbixClient.Authenticate(context.Background())
assert.NoError(t, err)
assert.Equal(t, "secretauth", zabbixClient.api.GetAuth())
@@ -27,7 +28,7 @@ func TestLogin(t *testing.T) {
func TestLoginError(t *testing.T) {
zabbixClient, _ := MockZabbixClient(basicDatasourceInfo, `{"result":""}`, 500)
err := zabbixClient.Login(context.Background())
err := zabbixClient.Authenticate(context.Background())
assert.Error(t, err)
assert.Equal(t, "", zabbixClient.api.GetAuth())

View File

@@ -13,8 +13,9 @@ import (
"github.com/alexanderzobnin/grafana-zabbix/pkg/metrics"
"github.com/bitly/go-simplejson"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"golang.org/x/net/context/ctxhttp"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
)
var (
@@ -168,6 +169,15 @@ func (api *ZabbixAPI) Authenticate(ctx context.Context, username string, passwor
return nil
}
// AuthenticateWithToken performs authentication with API token.
func (api *ZabbixAPI) AuthenticateWithToken(ctx context.Context, token string) error {
if token == "" {
return errors.New("API token is empty")
}
api.SetAuth(token)
return nil
}
func isDeprecatedUserParamError(err error) bool {
if err == nil {
return false