There was a misunderstanding of how goroutines work. Turns out, the goroutine uses the same keyring as the main routine, which can result in some conflicts depending on when the goroutine executes
241 lines
8.0 KiB
Go
241 lines
8.0 KiB
Go
package static_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cenkalti/backoff/v4"
|
|
"github.com/hashicorp/boundary/api/roles"
|
|
"github.com/hashicorp/boundary/api/sessions"
|
|
"github.com/hashicorp/boundary/api/users"
|
|
"github.com/hashicorp/boundary/testing/internal/e2e"
|
|
"github.com/hashicorp/boundary/testing/internal/e2e/boundary"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestCliSessionCancelUser uses the cli to create a new user and sets up the right permissions for
|
|
// the user to connect to the created target. The test also confirms that an admin can cancel the
|
|
// user's session.
|
|
func TestCliSessionCancelUser(t *testing.T) {
|
|
e2e.MaybeSkipTest(t)
|
|
c, err := loadConfig()
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
boundary.AuthenticateAdminCli(t, ctx)
|
|
newOrgId := boundary.CreateNewOrgCli(t, ctx)
|
|
newProjectId := boundary.CreateNewProjectCli(t, ctx, newOrgId)
|
|
newHostCatalogId := boundary.CreateNewHostCatalogCli(t, ctx, newProjectId)
|
|
newHostSetId := boundary.CreateNewHostSetCli(t, ctx, newHostCatalogId)
|
|
newHostId := boundary.CreateNewHostCli(t, ctx, newHostCatalogId, c.TargetIp)
|
|
boundary.AddHostToHostSetCli(t, ctx, newHostSetId, newHostId)
|
|
newTargetId := boundary.CreateNewTargetCli(t, ctx, newProjectId, c.TargetPort)
|
|
boundary.AddHostSourceToTargetCli(t, ctx, newTargetId, newHostSetId)
|
|
acctName := "e2e-account"
|
|
newAccountId, acctPassword := boundary.CreateNewAccountCli(t, ctx, acctName)
|
|
newUserId := boundary.CreateNewUserCli(t, ctx, "global")
|
|
boundary.SetAccountToUserCli(t, ctx, newUserId, newAccountId)
|
|
|
|
// Try to connect to the target as a user without permissions
|
|
boundary.AuthenticateCli(t, ctx, acctName, acctPassword)
|
|
output := e2e.RunCommand(ctx, "boundary",
|
|
e2e.WithArgs(
|
|
"connect",
|
|
"-target-id", newTargetId,
|
|
"-format", "json",
|
|
"-exec", "/usr/bin/ssh", "--",
|
|
"-l", c.TargetSshUser,
|
|
"-i", c.TargetSshKeyPath,
|
|
"-o", "UserKnownHostsFile=/dev/null",
|
|
"-o", "StrictHostKeyChecking=no",
|
|
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
|
|
"-p", "{{boundary.port}}", // this is provided by boundary
|
|
"{{boundary.ip}}",
|
|
"hostname -i",
|
|
),
|
|
)
|
|
require.Error(t, output.Err, string(output.Stdout), string(output.Stderr))
|
|
var response e2e.CliError
|
|
err = json.Unmarshal(output.Stderr, &response)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 403, response.Status)
|
|
t.Log("Successfully received an error when connecting to target as a user without permissions")
|
|
|
|
// Create a role
|
|
boundary.AuthenticateAdminCli(t, ctx)
|
|
output = e2e.RunCommand(ctx, "boundary",
|
|
e2e.WithArgs(
|
|
"roles", "create",
|
|
"-scope-id", newProjectId,
|
|
"-name", "e2e Role",
|
|
"-format", "json",
|
|
),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
var newRoleResult roles.RoleCreateResult
|
|
err = json.Unmarshal(output.Stdout, &newRoleResult)
|
|
require.NoError(t, err)
|
|
newRoleId := newRoleResult.Item.Id
|
|
t.Logf("Created Role: %s", newRoleId)
|
|
|
|
// Add grant to role
|
|
output = e2e.RunCommand(ctx, "boundary",
|
|
e2e.WithArgs(
|
|
"roles", "add-grants",
|
|
"-id", newRoleId,
|
|
"-grant", "id=*;type=target;actions=authorize-session",
|
|
),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
|
|
// Add user to role
|
|
output = e2e.RunCommand(ctx, "boundary",
|
|
e2e.WithArgs(
|
|
"roles", "add-principals",
|
|
"-id", newRoleId,
|
|
"-principal", newUserId,
|
|
),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
|
|
// Connect to target to create a session
|
|
ctxCancel, cancel := context.WithCancel(context.Background())
|
|
errChan := make(chan *e2e.CommandResult)
|
|
go func() {
|
|
token := boundary.GetAuthenticationTokenCli(t, ctx, acctName, acctPassword)
|
|
t.Log("Starting session as user...")
|
|
errChan <- e2e.RunCommand(ctxCancel, "boundary",
|
|
e2e.WithArgs(
|
|
"connect",
|
|
"-token", "env://E2E_AUTH_TOKEN",
|
|
"-target-id", newTargetId,
|
|
"-exec", "/usr/bin/ssh", "--",
|
|
"-l", c.TargetSshUser,
|
|
"-i", c.TargetSshKeyPath,
|
|
"-o", "UserKnownHostsFile=/dev/null",
|
|
"-o", "StrictHostKeyChecking=no",
|
|
"-o", "IdentitiesOnly=yes", // forces the use of the provided key
|
|
"-p", "{{boundary.port}}", // this is provided by boundary
|
|
"{{boundary.ip}}",
|
|
"hostname -i; sleep 60",
|
|
),
|
|
e2e.WithEnv("E2E_AUTH_TOKEN", token),
|
|
)
|
|
}()
|
|
t.Cleanup(cancel)
|
|
|
|
// Get list of sessions
|
|
var session *sessions.Session
|
|
err = backoff.RetryNotify(
|
|
func() error {
|
|
output := e2e.RunCommand(ctx, "boundary",
|
|
e2e.WithArgs("sessions", "list", "-scope-id", newProjectId, "-format", "json"),
|
|
)
|
|
if output.Err != nil {
|
|
return backoff.Permanent(errors.New(string(output.Stderr)))
|
|
}
|
|
|
|
var sessionListResult sessions.SessionListResult
|
|
err = json.Unmarshal(output.Stdout, &sessionListResult)
|
|
if err != nil {
|
|
return backoff.Permanent(err)
|
|
}
|
|
|
|
sessionCount := len(sessionListResult.Items)
|
|
if sessionCount == 0 {
|
|
return errors.New("No items are appearing in the session list")
|
|
}
|
|
|
|
t.Logf("Found %d session(s)", sessionCount)
|
|
if sessionCount != 1 {
|
|
return backoff.Permanent(errors.New("Only one session was expected to be found"))
|
|
}
|
|
|
|
session = sessionListResult.Items[0]
|
|
return nil
|
|
},
|
|
backoff.WithMaxRetries(backoff.NewConstantBackOff(3*time.Second), 5),
|
|
func(err error, td time.Duration) {
|
|
t.Logf("%s. Retrying...", err.Error())
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, newTargetId, session.TargetId)
|
|
assert.Equal(t, newHostId, session.HostId)
|
|
require.Equal(t, "active", session.Status)
|
|
|
|
// Cancel session
|
|
output = e2e.RunCommand(ctx, "boundary",
|
|
e2e.WithArgs("sessions", "cancel", "-id", session.Id),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
|
|
output = e2e.RunCommand(ctx, "boundary",
|
|
e2e.WithArgs("sessions", "read", "-id", session.Id, "-format", "json"),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
var newSessionReadResult sessions.SessionReadResult
|
|
err = json.Unmarshal(output.Stdout, &newSessionReadResult)
|
|
require.NoError(t, err)
|
|
require.Condition(t, func() bool {
|
|
return newSessionReadResult.Item.Status == "canceling" || newSessionReadResult.Item.Status == "terminated"
|
|
})
|
|
|
|
// Check output from session
|
|
select {
|
|
case output := <-errChan:
|
|
// `boundary connect` returns a 255 when cancelled
|
|
require.Equal(t, output.ExitCode, 255, string(output.Stdout), string(output.Stderr))
|
|
case <-time.After(time.Second * 5):
|
|
t.Fatal("Timed out waiting for session command to exit")
|
|
}
|
|
|
|
t.Log("Successfully cancelled session")
|
|
}
|
|
|
|
// TestApiCreateUser uses the Go api to create a new user and add some grants to the user
|
|
func TestApiCreateUser(t *testing.T) {
|
|
e2e.MaybeSkipTest(t)
|
|
c, err := loadConfig()
|
|
require.NoError(t, err)
|
|
|
|
client, err := boundary.NewApiClient()
|
|
require.NoError(t, err)
|
|
ctx := context.Background()
|
|
|
|
newOrgId := boundary.CreateNewOrgApi(t, ctx, client)
|
|
newProjectId := boundary.CreateNewProjectApi(t, ctx, client, newOrgId)
|
|
newHostCatalogId := boundary.CreateNewHostCatalogApi(t, ctx, client, newProjectId)
|
|
newHostSetId := boundary.CreateNewHostSetApi(t, ctx, client, newHostCatalogId)
|
|
newHostId := boundary.CreateNewHostApi(t, ctx, client, newHostCatalogId, c.TargetIp)
|
|
boundary.AddHostToHostSetApi(t, ctx, client, newHostSetId, newHostId)
|
|
newTargetId := boundary.CreateNewTargetApi(t, ctx, client, newProjectId, c.TargetPort)
|
|
boundary.AddHostSourceToTargetApi(t, ctx, client, newTargetId, newHostSetId)
|
|
|
|
acctName := "e2e-account"
|
|
newAcctId, _ := boundary.CreateNewAccountApi(t, ctx, client, acctName)
|
|
newUserId := boundary.CreateNewUserApi(t, ctx, client, "global")
|
|
uClient := users.NewClient(client)
|
|
uClient.SetAccounts(ctx, newUserId, 0, []string{newAcctId})
|
|
|
|
rClient := roles.NewClient(client)
|
|
newRoleResult, err := rClient.Create(ctx, newProjectId)
|
|
require.NoError(t, err)
|
|
newRoleId := newRoleResult.Item.Id
|
|
t.Logf("Create Role: %s", newRoleId)
|
|
|
|
_, err = rClient.AddGrants(ctx, newRoleId, 0, []string{"id=*;type=target;actions=authorize-session"},
|
|
roles.WithAutomaticVersioning(true),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = rClient.AddPrincipals(ctx, newRoleId, 0, []string{newUserId},
|
|
roles.WithAutomaticVersioning(true),
|
|
)
|
|
require.NoError(t, err)
|
|
}
|