AuthN: Add auth proxy client (#61555)
* AuthN: set up boilerplate for proxy client * AuthN: Implement Test for proxy client * AuthN: parse accept list in constructor * AuthN: add proxy client interface * AuthN: handle error * AuthN: Implement the proxy client interface for ldap * AuthN: change reciever name * AuthN: add grafana as a proxy client * AuthN: for error returned * AuthN: add tests for grafana proxy auth * AuthN: swap order of grafan and ldap auth * AuthN: Parse additional proxy headers in proxy client and pass down
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
package clients
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestProxy_Authenticate(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
req *authn.Request
|
||||
ips string
|
||||
proxyHeader string
|
||||
proxyHeaders map[string]string
|
||||
expectedErr error
|
||||
expectedUsername string
|
||||
expectedAdditional map[string]string
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
desc: "should authenticate using passed in proxy client",
|
||||
ips: "127.0.0.1",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{
|
||||
Header: map[string][]string{
|
||||
"X-Username": {"username"},
|
||||
"X-Name": {"name"},
|
||||
"X-Email": {"email"},
|
||||
"X-Login": {"login"},
|
||||
"X-Role": {"Viewer"},
|
||||
"X-Group": {"grp1,grp2"},
|
||||
},
|
||||
RemoteAddr: "127.0.0.1:333",
|
||||
},
|
||||
},
|
||||
proxyHeader: "X-Username",
|
||||
proxyHeaders: map[string]string{
|
||||
proxyFieldName: "X-Name",
|
||||
proxyFieldEmail: "X-Email",
|
||||
proxyFieldLogin: "X-Login",
|
||||
proxyFieldRole: "X-Role",
|
||||
proxyFieldGroups: "X-Group",
|
||||
},
|
||||
expectedUsername: "username",
|
||||
expectedAdditional: map[string]string{
|
||||
proxyFieldName: "name",
|
||||
proxyFieldEmail: "email",
|
||||
proxyFieldLogin: "login",
|
||||
proxyFieldRole: "Viewer",
|
||||
proxyFieldGroups: "grp1,grp2",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "should fail when proxy header is empty",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{Header: map[string][]string{
|
||||
"X-Username": {""},
|
||||
"X-Name": {"name"},
|
||||
"X-Email": {"email"},
|
||||
"X-Login": {"login"},
|
||||
"X-Role": {"Viewer"},
|
||||
"X-Group": {"grp1,grp2"},
|
||||
}},
|
||||
},
|
||||
proxyHeader: "X-Username",
|
||||
proxyHeaders: map[string]string{
|
||||
proxyFieldName: "X-Name",
|
||||
proxyFieldEmail: "X-Email",
|
||||
proxyFieldLogin: "X-Login",
|
||||
proxyFieldRole: "X-Role",
|
||||
proxyFieldGroups: "X-Group",
|
||||
},
|
||||
expectedErr: errEmptyProxyHeader,
|
||||
},
|
||||
{
|
||||
desc: "should fail when caller ip is not in accept list",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{
|
||||
Header: map[string][]string{},
|
||||
RemoteAddr: "127.0.0.2:333",
|
||||
},
|
||||
},
|
||||
ips: "127.0.0.1",
|
||||
expectedErr: errNotAcceptedIP,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.AuthProxyHeaderName = "X-Username"
|
||||
cfg.AuthProxyHeaders = tt.proxyHeaders
|
||||
cfg.AuthProxyWhitelist = tt.ips
|
||||
|
||||
calledUsername := ""
|
||||
var calledAdditional map[string]string
|
||||
|
||||
proxyClient := authntest.MockProxyClient{AuthenticateProxyFunc: func(ctx context.Context, r *authn.Request, username string, additional map[string]string) (*authn.Identity, error) {
|
||||
calledUsername = username
|
||||
calledAdditional = additional
|
||||
return nil, nil
|
||||
}}
|
||||
c, err := ProvideProxy(cfg, proxyClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = c.Authenticate(context.Background(), tt.req)
|
||||
assert.ErrorIs(t, err, tt.expectedErr)
|
||||
assert.Equal(t, tt.expectedUsername, calledUsername)
|
||||
assert.EqualValues(t, tt.expectedAdditional, calledAdditional)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxy_Test(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
req *authn.Request
|
||||
expectedOK bool
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
desc: "should return true when proxy header exists",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{
|
||||
Header: map[string][]string{"Proxy-Header": {"some value"}},
|
||||
},
|
||||
},
|
||||
expectedOK: true,
|
||||
},
|
||||
{
|
||||
desc: "should return false when proxy header exists but has no value",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{
|
||||
Header: map[string][]string{"Proxy-Header": {""}},
|
||||
},
|
||||
},
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
desc: "should return false when no proxy header is set on request",
|
||||
req: &authn.Request{
|
||||
HTTPRequest: &http.Request{Header: map[string][]string{}},
|
||||
},
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
desc: "should return false when no http request is present",
|
||||
req: &authn.Request{},
|
||||
expectedOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.AuthProxyHeaderName = "Proxy-Header"
|
||||
|
||||
c, _ := ProvideProxy(cfg, nil)
|
||||
assert.Equal(t, tt.expectedOK, c.Test(context.Background(), tt.req))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user