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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user