Files
teleport/lib/web/join_tokens_test.go
T
Matheus a73bcc485b Add endpoint to webapi to generate DB join token (#10914)
* Add endpoint to generate join tokens with any roles
2022-03-14 16:18:18 -03:00

304 lines
7.8 KiB
Go

/*
Copyright 2015-2022 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package web
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/fixtures"
"github.com/gravitational/trace"
)
func TestCreateNodeJoinToken(t *testing.T) {
m := &mockedNodeAPIGetter{}
m.mockGenerateToken = func(ctx context.Context, req auth.GenerateTokenRequest) (string, error) {
return "some-token-id", nil
}
token, err := createJoinToken(context.Background(), m, types.SystemRoles{
types.RoleNode,
types.RoleApp,
})
require.NoError(t, err)
require.Equal(t, defaults.NodeJoinTokenTTL, token.Expiry.Sub(time.Now().UTC()).Round(time.Second))
require.Equal(t, "some-token-id", token.ID)
}
func TestGetNodeJoinScript(t *testing.T) {
m := &mockedNodeAPIGetter{}
m.mockGetProxyServers = func() ([]types.Server, error) {
var s types.ServerV2
s.SetPublicAddr("test-host:12345678")
return []types.Server{&s}, nil
}
m.mockGetClusterCACert = func() (*auth.LocalCAResponse, error) {
fakeBytes := []byte(fixtures.SigningCertPEM)
return &auth.LocalCAResponse{TLSCA: fakeBytes}, nil
}
nilTokenLength := scriptSettings{
token: "",
}
shortTokenLength := scriptSettings{
token: "f18da1c9f6630a51e8daf121e7451d",
}
testTokenID := "f18da1c9f6630a51e8daf121e7451daa"
validTokenLength := scriptSettings{
token: testTokenID,
}
// Test zero-value initialization.
script, err := getJoinScript(scriptSettings{}, m)
require.Empty(t, script)
require.True(t, trace.IsBadParameter(err))
// Test bad token lengths.
script, err = getJoinScript(nilTokenLength, m)
require.Empty(t, script)
require.True(t, trace.IsBadParameter(err))
script, err = getJoinScript(shortTokenLength, m)
require.Empty(t, script)
require.True(t, trace.IsBadParameter(err))
// Test valid token format.
script, err = getJoinScript(validTokenLength, m)
require.NoError(t, err)
require.Contains(t, script, testTokenID)
require.Contains(t, script, "test-host")
require.Contains(t, script, "12345678")
require.Contains(t, script, "sha256:")
}
func TestGetAppJoinScript(t *testing.T) {
m := &mockedNodeAPIGetter{}
m.mockGetProxyServers = func() ([]types.Server, error) {
var s types.ServerV2
s.SetPublicAddr("test-host:12345678")
return []types.Server{&s}, nil
}
m.mockGetClusterCACert = func() (*auth.LocalCAResponse, error) {
fakeBytes := []byte(fixtures.SigningCertPEM)
return &auth.LocalCAResponse{TLSCA: fakeBytes}, nil
}
testTokenID := "f18da1c9f6630a51e8daf121e7451daa"
badAppName := scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "",
appURI: "127.0.0.1:0",
}
badAppURI := scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "test-app",
appURI: "",
}
// Test invalid app data.
script, err := getJoinScript(badAppName, m)
require.Empty(t, script)
require.True(t, trace.IsBadParameter(err))
script, err = getJoinScript(badAppURI, m)
require.Empty(t, script)
require.True(t, trace.IsBadParameter(err))
// Test various 'good' cases.
expectedOutputs := []string{
testTokenID,
"test-host",
"12345678",
"sha256:",
}
tests := []struct {
desc string
settings scriptSettings
shouldError bool
outputs []string
}{
{
desc: "node only join mode with other values not provided",
settings: scriptSettings{
token: testTokenID,
appInstallMode: false,
},
outputs: expectedOutputs,
},
{
desc: "node only join mode with values set to blank",
settings: scriptSettings{
token: testTokenID,
appInstallMode: false,
appName: "",
appURI: "",
},
outputs: expectedOutputs,
},
{
desc: "all settings set correctly",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "test-app123",
appURI: "http://localhost:12345/landing page__",
},
outputs: append(
expectedOutputs,
"test-app123",
"http://localhost:12345",
),
},
{
desc: "all settings set correctly with a longer app name",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "this-is-a-much-longer-app-name-being-used-for-testing",
appURI: "https://1.2.3.4:54321",
},
outputs: append(
expectedOutputs,
"this-is-a-much-longer-app-name-being-used-for-testing",
"https://1.2.3.4:54321",
),
},
{
desc: "app name containing double quotes is rejected",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: `ab"cd`,
appURI: "https://1.2.3.4:54321",
},
shouldError: true,
},
{
desc: "app URI containing double quotes is rejected",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "abcd",
appURI: `https://1.2.3.4:54321/x"y"z`,
},
shouldError: true,
},
{
desc: "app name containing a backtick is rejected",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "ab`whoami`cd",
appURI: "https://1.2.3.4:54321",
},
shouldError: true,
},
{
desc: "app URI containing a backtick is rejected",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "abcd",
appURI: "https://1.2.3.4:54321/`whoami`",
},
shouldError: true,
},
{
desc: "app name containing a dollar sign is rejected",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "ab$HOME",
appURI: "https://1.2.3.4:54321",
},
shouldError: true,
},
{
desc: "app URI containing a dollar sign is rejected",
settings: scriptSettings{
token: testTokenID,
appInstallMode: true,
appName: "abcd",
appURI: "https://1.2.3.4:54321/$HOME",
},
shouldError: true,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.desc, func(t *testing.T) {
script, err = getJoinScript(tc.settings, m)
if tc.shouldError {
require.NotNil(t, err)
require.Equal(t, script, "")
} else {
require.NoError(t, err)
for _, output := range tc.outputs {
require.Contains(t, script, output)
}
}
})
}
}
type mockedNodeAPIGetter struct {
mockGenerateToken func(ctx context.Context, req auth.GenerateTokenRequest) (string, error)
mockGetProxyServers func() ([]types.Server, error)
mockGetClusterCACert func() (*auth.LocalCAResponse, error)
}
func (m *mockedNodeAPIGetter) GenerateToken(ctx context.Context, req auth.GenerateTokenRequest) (string, error) {
if m.mockGenerateToken != nil {
return m.mockGenerateToken(ctx, req)
}
return "", trace.NotImplemented("mockGenerateToken not implemented")
}
func (m *mockedNodeAPIGetter) GetProxies() ([]types.Server, error) {
if m.mockGetProxyServers != nil {
return m.mockGetProxyServers()
}
return nil, trace.NotImplemented("mockGetProxyServers not implemented")
}
func (m *mockedNodeAPIGetter) GetClusterCACert() (*auth.LocalCAResponse, error) {
if m.mockGetClusterCACert != nil {
return m.mockGetClusterCACert()
}
return nil, trace.NotImplemented("mockGetClusterCACert not implemented")
}