mirror of https://github.com/hashicorp/boundary
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
592 lines
16 KiB
592 lines
16 KiB
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package sessionrecordingscmd
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/boundary/api"
|
|
"github.com/hashicorp/boundary/api/scopes"
|
|
"github.com/hashicorp/boundary/api/sessionrecordings"
|
|
"github.com/hashicorp/boundary/internal/cmd/base"
|
|
"github.com/hashicorp/boundary/internal/recording"
|
|
)
|
|
|
|
type extraCmdVars struct{}
|
|
|
|
func (c *Command) extraHelpFunc(helpMap map[string]func() string) string {
|
|
var helpStr string
|
|
switch c.Func {
|
|
case "":
|
|
return base.WrapForHelpText([]string{
|
|
"Usage: boundary session-recordings [sub command] [options] [args]",
|
|
"",
|
|
" This command allows operations on Boundary session recordings.",
|
|
"",
|
|
" Read a session recording:",
|
|
"",
|
|
` $ boundary session-recordings read -id s_1234567890`,
|
|
"",
|
|
" List session recording:",
|
|
"",
|
|
` $ boundary session-recordings list -scope-id global`,
|
|
"",
|
|
" Download a session recording:",
|
|
"",
|
|
` $ boundary session-recordings download -id chr_1234567890`,
|
|
"",
|
|
|
|
" Please see the sessions subcommand help for detailed usage information.",
|
|
})
|
|
|
|
default:
|
|
helpStr = helpMap["base"]()
|
|
}
|
|
|
|
return helpStr + c.Flags().Help()
|
|
}
|
|
|
|
func (c *Command) printListTable(items []*sessionrecordings.SessionRecording) string {
|
|
if len(items) == 0 {
|
|
return "No session recordings found"
|
|
}
|
|
var output []string
|
|
output = []string{
|
|
"",
|
|
"Session Recording information:",
|
|
}
|
|
for i, item := range items {
|
|
if i > 0 {
|
|
output = append(output, "")
|
|
}
|
|
if item.Id != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" ID: %s", item.Id),
|
|
)
|
|
} else {
|
|
output = append(output,
|
|
fmt.Sprintf(" ID: %s", "(not available)"),
|
|
)
|
|
}
|
|
if c.FlagRecursive && item.Scope.Id != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Scope ID: %s", item.Scope.Id),
|
|
)
|
|
}
|
|
if item.SessionId != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Session ID: %s", item.SessionId),
|
|
)
|
|
}
|
|
if item.StorageBucketId != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Storage Bucket ID: %s", item.StorageBucketId),
|
|
)
|
|
}
|
|
if !item.CreatedTime.IsZero() {
|
|
output = append(output,
|
|
fmt.Sprintf(" Created Time: %s", item.CreatedTime.Local().Format(time.RFC1123)),
|
|
)
|
|
}
|
|
if !item.UpdatedTime.IsZero() {
|
|
output = append(output,
|
|
fmt.Sprintf(" Updated Time: %s", item.UpdatedTime.Local().Format(time.RFC1123)),
|
|
)
|
|
}
|
|
if !item.StartTime.IsZero() {
|
|
output = append(output,
|
|
fmt.Sprintf(" Start Time: %s", item.StartTime.Local().Format(time.RFC1123)),
|
|
)
|
|
}
|
|
if !item.EndTime.IsZero() {
|
|
output = append(output,
|
|
fmt.Sprintf(" End Time: %s", item.EndTime.Local().Format(time.RFC1123)),
|
|
)
|
|
}
|
|
if item.Type != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Type: %s", item.Type),
|
|
)
|
|
}
|
|
if item.State != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" State: %s", item.State),
|
|
)
|
|
}
|
|
if !item.RetainUntil.IsZero() {
|
|
var retention string
|
|
switch item.RetainUntil {
|
|
case recording.InfinityTS:
|
|
retention = "Forever"
|
|
default:
|
|
retention = item.RetainUntil.Local().Format(time.RFC1123)
|
|
}
|
|
output = append(output,
|
|
fmt.Sprintf(" Retain Until: %s", retention),
|
|
)
|
|
}
|
|
if !item.DeleteAfter.IsZero() {
|
|
output = append(output,
|
|
fmt.Sprintf(" Delete After: %s", item.DeleteAfter.Local().Format(time.RFC1123)),
|
|
)
|
|
}
|
|
if len(item.AuthorizedActions) > 0 {
|
|
output = append(output,
|
|
" Authorized Actions:",
|
|
base.WrapSlice(6, item.AuthorizedActions),
|
|
)
|
|
}
|
|
}
|
|
|
|
return base.WrapForHelpText(output)
|
|
}
|
|
|
|
func printItemTable(item *sessionrecordings.SessionRecording, resp *api.Response) string {
|
|
const (
|
|
durationKey = "Duration (Seconds)"
|
|
)
|
|
nonAttributeMap := map[string]any{}
|
|
if item.Id != "" {
|
|
nonAttributeMap["ID"] = item.Id
|
|
}
|
|
if item.Scope.Id != "" {
|
|
nonAttributeMap["Scope ID"] = item.Scope.Id
|
|
}
|
|
if item.SessionId != "" {
|
|
nonAttributeMap["Session ID"] = item.SessionId
|
|
}
|
|
if item.StorageBucketId != "" {
|
|
nonAttributeMap["Storage Bucket ID"] = item.StorageBucketId
|
|
}
|
|
if item.BytesUp != 0 {
|
|
nonAttributeMap["Bytes Up"] = item.BytesUp
|
|
}
|
|
if item.BytesDown != 0 {
|
|
nonAttributeMap["Bytes Down"] = item.BytesDown
|
|
}
|
|
if !item.CreatedTime.IsZero() {
|
|
nonAttributeMap["Created Time"] = item.CreatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !item.UpdatedTime.IsZero() {
|
|
nonAttributeMap["Updated Time"] = item.UpdatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !item.StartTime.IsZero() {
|
|
nonAttributeMap["Start Time"] = item.StartTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !item.EndTime.IsZero() {
|
|
nonAttributeMap["Updated Time"] = item.EndTime.Local().Format(time.RFC1123)
|
|
}
|
|
if item.Duration.Duration != 0 {
|
|
nonAttributeMap[durationKey] = item.Duration.Seconds()
|
|
}
|
|
if !item.RetainUntil.IsZero() {
|
|
var retention string
|
|
switch item.RetainUntil {
|
|
case recording.InfinityTS:
|
|
retention = "Forever"
|
|
default:
|
|
retention = item.RetainUntil.Local().Format(time.RFC1123)
|
|
}
|
|
nonAttributeMap["Retain Until"] = retention
|
|
}
|
|
if !item.DeleteAfter.IsZero() {
|
|
nonAttributeMap["Delete After"] = item.DeleteAfter.Local().Format(time.RFC1123)
|
|
}
|
|
if item.Type != "" {
|
|
nonAttributeMap["Type"] = item.Type
|
|
}
|
|
if item.State != "" {
|
|
nonAttributeMap["State"] = item.State
|
|
}
|
|
if item.ErrorDetails != "" {
|
|
nonAttributeMap["Error Details"] = item.ErrorDetails
|
|
}
|
|
if len(item.MimeTypes) > 0 {
|
|
nonAttributeMap["Mime Types"] = strings.Join(item.MimeTypes, ", ")
|
|
}
|
|
if item.Endpoint != "" {
|
|
nonAttributeMap["Endpoint"] = item.Endpoint
|
|
}
|
|
|
|
maxLength := base.MaxAttributesLength(nonAttributeMap, nil, nil)
|
|
|
|
ret := []string{
|
|
"",
|
|
"Session Recording information:",
|
|
base.WrapMap(2, maxLength+2, nonAttributeMap),
|
|
}
|
|
|
|
if item.Scope != nil {
|
|
ret = append(ret,
|
|
"",
|
|
" Scope:",
|
|
base.ScopeInfoForOutput(item.Scope, maxLength),
|
|
)
|
|
}
|
|
|
|
if len(item.AuthorizedActions) > 0 {
|
|
ret = append(ret,
|
|
"",
|
|
" Authorized Actions:",
|
|
base.WrapSlice(4, item.AuthorizedActions),
|
|
)
|
|
}
|
|
|
|
if item.CreateTimeValues != nil {
|
|
if item.CreateTimeValues.User != nil {
|
|
userMap := map[string]any{
|
|
"ID": item.CreateTimeValues.User.Id,
|
|
}
|
|
if item.CreateTimeValues.User.Name != "" {
|
|
userMap["Name"] = item.CreateTimeValues.User.Name
|
|
}
|
|
if item.CreateTimeValues.User.Description != "" {
|
|
userMap["Description"] = item.CreateTimeValues.User.Description
|
|
}
|
|
maxUserLength := base.MaxAttributesLength(userMap, nil, nil)
|
|
ret = append(ret,
|
|
"",
|
|
" User Info:",
|
|
base.WrapMap(4, maxUserLength+2, userMap),
|
|
" Scope:", customScopeInfoForOutput(item.CreateTimeValues.User.Scope, maxUserLength, 6),
|
|
)
|
|
}
|
|
if item.CreateTimeValues.Target != nil {
|
|
targetMap := map[string]any{
|
|
"ID": item.CreateTimeValues.Target.Id,
|
|
}
|
|
if item.CreateTimeValues.Target.Name != "" {
|
|
targetMap["Name"] = item.CreateTimeValues.Target.Name
|
|
}
|
|
if item.CreateTimeValues.Target.Description != "" {
|
|
targetMap["Description"] = item.CreateTimeValues.Target.Description
|
|
}
|
|
if item.CreateTimeValues.Target.SessionMaxSeconds != 0 {
|
|
targetMap["Session Max Seconds"] = item.CreateTimeValues.Target.SessionMaxSeconds
|
|
}
|
|
if item.CreateTimeValues.Target.SessionConnectionLimit != 0 {
|
|
targetMap["Session Connection Limit"] = item.CreateTimeValues.Target.SessionConnectionLimit
|
|
}
|
|
if item.CreateTimeValues.Target.WorkerFilter != "" {
|
|
targetMap["Worker Filter"] = item.CreateTimeValues.Target.WorkerFilter
|
|
}
|
|
if item.CreateTimeValues.Target.EgressWorkerFilter != "" {
|
|
targetMap["Egress Worker Filter"] = item.CreateTimeValues.Target.EgressWorkerFilter
|
|
}
|
|
if item.CreateTimeValues.Target.IngressWorkerFilter != "" {
|
|
targetMap["Ingress Worker Filter"] = item.CreateTimeValues.Target.IngressWorkerFilter
|
|
}
|
|
if item.CreateTimeValues.Target.Attributes != nil {
|
|
if attr, err := item.CreateTimeValues.Target.GetSshTargetAttributes(); err == nil && attr != nil && attr.DefaultPort != 0 {
|
|
targetMap["Default Port"] = attr.DefaultPort
|
|
}
|
|
}
|
|
maxTargetLength := base.MaxAttributesLength(targetMap, nil, nil)
|
|
ret = append(ret,
|
|
"",
|
|
" Target Info:",
|
|
base.WrapMap(4, maxTargetLength+2, targetMap),
|
|
" Scope:", customScopeInfoForOutput(item.CreateTimeValues.Target.Scope, maxTargetLength, 6),
|
|
)
|
|
}
|
|
if item.CreateTimeValues.Host != nil {
|
|
hostMap := map[string]any{
|
|
"ID": item.CreateTimeValues.Host.Id,
|
|
}
|
|
if item.CreateTimeValues.Host.Name != "" {
|
|
hostMap["Name"] = item.CreateTimeValues.Host.Name
|
|
}
|
|
if item.CreateTimeValues.Host.Description != "" {
|
|
hostMap["Description"] = item.CreateTimeValues.Host.Description
|
|
}
|
|
if item.CreateTimeValues.Host.Type != "" {
|
|
hostMap["Type"] = item.CreateTimeValues.Host.Type
|
|
}
|
|
if item.CreateTimeValues.Host.ExternalId != "" {
|
|
hostMap["External ID"] = item.CreateTimeValues.Host.ExternalId
|
|
}
|
|
if item.CreateTimeValues.Host.Attributes != nil {
|
|
if attr, err := item.CreateTimeValues.Host.GetStaticHostAttributes(); err == nil && attr != nil && attr.Address != "" {
|
|
hostMap["Address"] = attr.Address
|
|
}
|
|
}
|
|
maxHostLength := base.MaxAttributesLength(hostMap, nil, nil)
|
|
ret = append(ret,
|
|
"",
|
|
" Host Info:", base.WrapMap(4, maxHostLength+2, hostMap),
|
|
)
|
|
|
|
if item.CreateTimeValues.Host.HostCatalog != nil {
|
|
catMap := map[string]any{
|
|
"ID": item.CreateTimeValues.Host.HostCatalog.Id,
|
|
}
|
|
if item.CreateTimeValues.Host.HostCatalog.Name != "" {
|
|
hostMap["Name"] = item.CreateTimeValues.Host.HostCatalog.Name
|
|
}
|
|
if item.CreateTimeValues.Host.HostCatalog.Description != "" {
|
|
hostMap["Description"] = item.CreateTimeValues.Host.HostCatalog.Description
|
|
}
|
|
if item.CreateTimeValues.Host.HostCatalog.PluginId != "" {
|
|
hostMap["Plugin ID"] = item.CreateTimeValues.Host.HostCatalog.PluginId
|
|
}
|
|
if item.CreateTimeValues.Host.HostCatalog.Type != "" {
|
|
hostMap["Type"] = item.CreateTimeValues.Host.HostCatalog.Type
|
|
}
|
|
maxCatLength := base.MaxAttributesLength(catMap, nil, nil)
|
|
|
|
ret = append(ret,
|
|
" HostCatalog:", base.WrapMap(6, maxCatLength, catMap),
|
|
" Scope:", customScopeInfoForOutput(item.CreateTimeValues.Host.HostCatalog.Scope, maxHostLength, 8),
|
|
)
|
|
}
|
|
}
|
|
if len(item.CreateTimeValues.CredentialLibraries) > 0 {
|
|
ret = append(ret,
|
|
"",
|
|
" Credential Libraries:")
|
|
for _, cl := range item.CreateTimeValues.CredentialLibraries {
|
|
cm := map[string]any{
|
|
"ID": cl.Id,
|
|
}
|
|
if cl.Name != "" {
|
|
cm["Name"] = cl.Name
|
|
}
|
|
if cl.Description != "" {
|
|
cm["Description"] = cl.Description
|
|
}
|
|
if cl.Type != "" {
|
|
cm["Type"] = cl.Type
|
|
}
|
|
if len(cl.Purposes) > 0 {
|
|
cm["Purpose"] = strings.Join(cl.Purposes, ", ")
|
|
}
|
|
if attrs, _ := cl.GetVaultSSHCertificateCredentialLibraryAttributes(); attrs != nil {
|
|
if attrs.Path != "" {
|
|
cm["Vault Path"] = attrs.Path
|
|
}
|
|
if attrs.Username != "" {
|
|
cm["Username"] = attrs.Username
|
|
}
|
|
if attrs.KeyType != "" {
|
|
cm["Key Type"] = attrs.KeyType
|
|
}
|
|
if attrs.Ttl != "" {
|
|
cm["Ttl"] = attrs.Ttl
|
|
}
|
|
}
|
|
if attrs, _ := cl.GetVaultCredentialLibraryAttributes(); attrs != nil {
|
|
if attrs.Path != "" {
|
|
cm["Vault Path"] = attrs.Path
|
|
}
|
|
if attrs.HttpMethod != "" {
|
|
cm["Http Method"] = attrs.HttpMethod
|
|
}
|
|
if attrs.HttpRequestBody != "" {
|
|
cm["Http Request Body"] = attrs.HttpRequestBody
|
|
}
|
|
}
|
|
maxLibLength := base.MaxAttributesLength(cm, nil, nil)
|
|
ret = append(ret,
|
|
base.WrapMap(4, maxLibLength, cm),
|
|
"",
|
|
)
|
|
if cs := cl.CredentialStore; cs != nil {
|
|
csm := credStoreMap(cs)
|
|
maxStoreLength := base.MaxAttributesLength(csm, nil, nil)
|
|
ret = append(ret,
|
|
" Credential Store:",
|
|
base.WrapMap(6, maxStoreLength, csm),
|
|
"",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(item.CreateTimeValues.Credentials) > 0 {
|
|
ret = append(ret,
|
|
"",
|
|
" Credentials:")
|
|
for _, c := range item.CreateTimeValues.Credentials {
|
|
cm := map[string]any{
|
|
"ID": c.Id,
|
|
}
|
|
if c.Name != "" {
|
|
cm["Name"] = c.Name
|
|
}
|
|
if c.Description != "" {
|
|
cm["Description"] = c.Description
|
|
}
|
|
if c.Type != "" {
|
|
cm["Type"] = c.Type
|
|
}
|
|
if len(c.Purposes) > 0 {
|
|
cm["Purpose"] = strings.Join(c.Purposes, ", ")
|
|
}
|
|
if attrs, _ := c.GetJsonCredentialAttributes(); attrs != nil {
|
|
if attrs.ObjectHmac != "" {
|
|
cm["Object HMAC"] = attrs.ObjectHmac
|
|
}
|
|
}
|
|
if attrs, _ := c.GetUsernamePasswordCredentialAttributes(); attrs != nil {
|
|
if attrs.Username != "" {
|
|
cm["Username"] = attrs.Username
|
|
}
|
|
}
|
|
if attrs, _ := c.GetSshPrivateKeyCredentialAttributes(); attrs != nil {
|
|
if attrs.Username != "" {
|
|
cm["Username"] = attrs.Username
|
|
}
|
|
}
|
|
maxLibLength := base.MaxAttributesLength(cm, nil, nil)
|
|
ret = append(ret,
|
|
base.WrapMap(4, maxLibLength, cm),
|
|
"",
|
|
)
|
|
if cs := c.CredentialStore; cs != nil {
|
|
csm := credStoreMap(cs)
|
|
maxStoreLength := base.MaxAttributesLength(csm, nil, nil)
|
|
ret = append(ret,
|
|
" Credential Store:",
|
|
base.WrapMap(6, maxStoreLength, csm),
|
|
"",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(item.ConnectionRecordings) > 0 {
|
|
maxAttrLen := len(durationKey)
|
|
ret = append(ret,
|
|
"",
|
|
" Connections Recordings:",
|
|
)
|
|
for _, cr := range item.ConnectionRecordings {
|
|
cm := map[string]any{
|
|
"ID": cr.Id,
|
|
}
|
|
if cr.BytesUp != 0 {
|
|
cm["Bytes Up"] = cr.BytesUp
|
|
}
|
|
if cr.BytesDown != 0 {
|
|
cm["Bytes Down"] = cr.BytesDown
|
|
}
|
|
if !cr.CreatedTime.IsZero() {
|
|
cm["Created Time"] = cr.CreatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !cr.UpdatedTime.IsZero() {
|
|
cm["Updated Time"] = cr.UpdatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !cr.StartTime.IsZero() {
|
|
cm["Start Time"] = cr.StartTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !cr.EndTime.IsZero() {
|
|
cm["End Time"] = cr.EndTime.Local().Format(time.RFC1123)
|
|
}
|
|
if cr.Duration.Duration != 0 {
|
|
cm[durationKey] = cr.Duration.Seconds()
|
|
}
|
|
if len(cr.MimeTypes) > 0 {
|
|
cm["Mime Types"] = strings.Join(cr.MimeTypes, ", ")
|
|
}
|
|
ret = append(ret,
|
|
base.WrapMap(4, maxAttrLen, cm),
|
|
"",
|
|
)
|
|
|
|
if len(cr.ChannelRecordings) > 0 {
|
|
var channelRecordings []map[string]any
|
|
for _, chr := range cr.ChannelRecordings {
|
|
chrm := map[string]any{
|
|
"ID": chr.Id,
|
|
}
|
|
if chr.BytesUp != 0 {
|
|
chrm["Bytes Up"] = chr.BytesUp
|
|
}
|
|
if chr.BytesDown != 0 {
|
|
chrm["Bytes Down"] = chr.BytesDown
|
|
}
|
|
if !chr.CreatedTime.IsZero() {
|
|
chrm["Created Time"] = chr.CreatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !chr.UpdatedTime.IsZero() {
|
|
chrm["Updated Time"] = chr.UpdatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !chr.StartTime.IsZero() {
|
|
chrm["Start Time"] = chr.StartTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !chr.EndTime.IsZero() {
|
|
chrm["End Time"] = chr.EndTime.Local().Format(time.RFC1123)
|
|
}
|
|
if chr.Duration.Duration != 0 {
|
|
chrm[durationKey] = chr.Duration.Seconds()
|
|
}
|
|
if len(chr.MimeTypes) > 0 {
|
|
chrm["Mime Types"] = strings.Join(chr.MimeTypes, ", ")
|
|
}
|
|
channelRecordings = append(channelRecordings, chrm)
|
|
}
|
|
ret = append(ret,
|
|
"",
|
|
" Channel Recordings:",
|
|
)
|
|
for _, cr := range channelRecordings {
|
|
ret = append(ret,
|
|
base.WrapMap(6, maxAttrLen, cr),
|
|
"",
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return base.WrapForHelpText(ret)
|
|
}
|
|
|
|
func credStoreMap(cs *sessionrecordings.CredentialStore) map[string]any {
|
|
csm := map[string]any{
|
|
"ID": cs.Id,
|
|
}
|
|
if cs.Name != "" {
|
|
csm["Name"] = cs.Name
|
|
}
|
|
if cs.Description != "" {
|
|
csm["Description"] = cs.Description
|
|
}
|
|
if cs.ScopeId != "" {
|
|
csm["Scope ID"] = cs.ScopeId
|
|
}
|
|
if cs.Type != "" {
|
|
csm["Type"] = cs.Type
|
|
}
|
|
if attrs, _ := cs.GetVaultCredentialStoreAttributes(); attrs != nil {
|
|
if attrs.Address != "" {
|
|
csm["Vault Address"] = attrs.Address
|
|
}
|
|
if attrs.Namespace != "" {
|
|
csm["Namespace"] = attrs.Namespace
|
|
}
|
|
if attrs.WorkerFilter != "" {
|
|
csm["Worker Filter"] = attrs.WorkerFilter
|
|
}
|
|
}
|
|
return csm
|
|
}
|
|
|
|
func customScopeInfoForOutput(scp *scopes.ScopeInfo, maxLength int, prefixSpaces int) string {
|
|
if scp == nil {
|
|
return " <not included in response>"
|
|
}
|
|
vals := map[string]any{
|
|
"ID": scp.Id,
|
|
"Type": scp.Type,
|
|
"Name": scp.Name,
|
|
}
|
|
if scp.ParentScopeId != "" {
|
|
vals["Parent Scope ID"] = scp.ParentScopeId
|
|
}
|
|
return base.WrapMap(prefixSpaces, maxLength, vals)
|
|
}
|