github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/desktop.go (about)

     1  /*
     2  Copyright 2021 Gravitational, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package types
    18  
    19  import (
    20  	"sort"
    21  	"strings"
    22  
    23  	"github.com/gravitational/trace"
    24  
    25  	"github.com/gravitational/teleport/api/types/compare"
    26  	"github.com/gravitational/teleport/api/utils"
    27  )
    28  
    29  const (
    30  	MaxRDPScreenWidth  = 8192
    31  	MaxRDPScreenHeight = 8192
    32  )
    33  
    34  var _ compare.IsEqual[WindowsDesktop] = (*WindowsDesktopV3)(nil)
    35  
    36  // WindowsDesktopService represents a Windows desktop service instance.
    37  type WindowsDesktopService interface {
    38  	// ResourceWithLabels provides common resource methods.
    39  	ResourceWithLabels
    40  	// GetAddr returns the network address of this service.
    41  	GetAddr() string
    42  	// GetVersion returns the teleport binary version of this service.
    43  	GetTeleportVersion() string
    44  	// GetHostname returns the hostname of this service
    45  	GetHostname() string
    46  	// ProxiedService provides common methods for a proxied service.
    47  	ProxiedService
    48  }
    49  
    50  type WindowsDesktopServices []WindowsDesktopService
    51  
    52  // AsResources returns windows desktops as type resources with labels.
    53  func (s WindowsDesktopServices) AsResources() []ResourceWithLabels {
    54  	resources := make([]ResourceWithLabels, 0, len(s))
    55  	for _, server := range s {
    56  		resources = append(resources, ResourceWithLabels(server))
    57  	}
    58  	return resources
    59  }
    60  
    61  var _ WindowsDesktopService = &WindowsDesktopServiceV3{}
    62  
    63  // NewWindowsDesktopServiceV3 creates a new WindowsDesktopServiceV3 resource.
    64  func NewWindowsDesktopServiceV3(meta Metadata, spec WindowsDesktopServiceSpecV3) (*WindowsDesktopServiceV3, error) {
    65  	s := &WindowsDesktopServiceV3{
    66  		ResourceHeader: ResourceHeader{
    67  			Metadata: meta,
    68  		},
    69  		Spec: spec,
    70  	}
    71  	if err := s.CheckAndSetDefaults(); err != nil {
    72  		return nil, trace.Wrap(err)
    73  	}
    74  	return s, nil
    75  }
    76  
    77  func (s *WindowsDesktopServiceV3) setStaticFields() {
    78  	s.Kind = KindWindowsDesktopService
    79  	s.Version = V3
    80  }
    81  
    82  // CheckAndSetDefaults checks and sets default values for any missing fields.
    83  func (s *WindowsDesktopServiceV3) CheckAndSetDefaults() error {
    84  	if s.Spec.Addr == "" {
    85  		return trace.BadParameter("WindowsDesktopServiceV3.Spec missing Addr field")
    86  	}
    87  	if s.Spec.TeleportVersion == "" {
    88  		return trace.BadParameter("WindowsDesktopServiceV3.Spec missing TeleportVersion field")
    89  	}
    90  
    91  	s.setStaticFields()
    92  	if err := s.ResourceHeader.CheckAndSetDefaults(); err != nil {
    93  		return trace.Wrap(err)
    94  	}
    95  	return nil
    96  }
    97  
    98  // GetAddr returns the network address of this service.
    99  func (s *WindowsDesktopServiceV3) GetAddr() string {
   100  	return s.Spec.Addr
   101  }
   102  
   103  // GetTeleportVersion returns the teleport binary version of this service.
   104  func (s *WindowsDesktopServiceV3) GetTeleportVersion() string {
   105  	return s.Spec.TeleportVersion
   106  }
   107  
   108  // GetProxyID returns a list of proxy ids this server is connected to.
   109  func (s *WindowsDesktopServiceV3) GetProxyIDs() []string {
   110  	return s.Spec.ProxyIDs
   111  }
   112  
   113  // SetProxyID sets the proxy ids this server is connected to.
   114  func (s *WindowsDesktopServiceV3) SetProxyIDs(proxyIDs []string) {
   115  	s.Spec.ProxyIDs = proxyIDs
   116  }
   117  
   118  // GetHostname returns the windows hostname of this service.
   119  func (s *WindowsDesktopServiceV3) GetHostname() string {
   120  	return s.Spec.Hostname
   121  }
   122  
   123  // MatchSearch goes through select field values and tries to
   124  // match against the list of search values.
   125  func (s *WindowsDesktopServiceV3) MatchSearch(values []string) bool {
   126  	fieldVals := append(utils.MapToStrings(s.GetAllLabels()), s.GetName(), s.GetHostname())
   127  	return MatchSearch(fieldVals, values, nil)
   128  }
   129  
   130  // WindowsDesktop represents a Windows desktop host.
   131  type WindowsDesktop interface {
   132  	// ResourceWithLabels provides common resource methods.
   133  	ResourceWithLabels
   134  	// GetAddr returns the network address of this host.
   135  	GetAddr() string
   136  	// GetDomain returns the ActiveDirectory domain of this host.
   137  	GetDomain() string
   138  	// GetHostID returns the ID of the Windows Desktop Service reporting the desktop.
   139  	GetHostID() string
   140  	// NonAD checks whether this is a standalone host that
   141  	// is not joined to an Active Directory domain.
   142  	NonAD() bool
   143  	// GetScreenSize returns the desired size of the screen to use for sessions
   144  	// to this host. Returns (0, 0) if no screen size is set, which means to
   145  	// use the size passed by the client over TDP.
   146  	GetScreenSize() (width, height uint32)
   147  	// Copy returns a copy of this windows desktop
   148  	Copy() *WindowsDesktopV3
   149  	// CloneResource returns a copy of the WindowDesktop as a ResourceWithLabels
   150  	CloneResource() ResourceWithLabels
   151  }
   152  
   153  var _ WindowsDesktop = &WindowsDesktopV3{}
   154  
   155  // NewWindowsDesktopV3 creates a new WindowsDesktopV3 resource.
   156  func NewWindowsDesktopV3(name string, labels map[string]string, spec WindowsDesktopSpecV3) (*WindowsDesktopV3, error) {
   157  	d := &WindowsDesktopV3{
   158  		ResourceHeader: ResourceHeader{
   159  			Metadata: Metadata{
   160  				Name:   name,
   161  				Labels: labels,
   162  			},
   163  		},
   164  		Spec: spec,
   165  	}
   166  	if err := d.CheckAndSetDefaults(); err != nil {
   167  		return nil, trace.Wrap(err)
   168  	}
   169  	return d, nil
   170  }
   171  
   172  func (d *WindowsDesktopV3) setStaticFields() {
   173  	d.Kind = KindWindowsDesktop
   174  	d.Version = V3
   175  }
   176  
   177  // CheckAndSetDefaults checks and sets default values for any missing fields.
   178  func (d *WindowsDesktopV3) CheckAndSetDefaults() error {
   179  	if d.Spec.Addr == "" {
   180  		return trace.BadParameter("WindowsDesktopV3.Spec missing Addr field")
   181  	}
   182  
   183  	// We use SNI to identify the desktop to route a connection to,
   184  	// and '.' will add an extra subdomain, preventing Teleport from
   185  	// correctly establishing TLS connections.
   186  	if name := d.GetName(); strings.Contains(name, ".") {
   187  		return trace.BadParameter("invalid name %q: desktop names cannot contain periods", name)
   188  	}
   189  
   190  	d.setStaticFields()
   191  	if err := d.ResourceHeader.CheckAndSetDefaults(); err != nil {
   192  		return trace.Wrap(err)
   193  	}
   194  
   195  	if d.Spec.ScreenSize != nil {
   196  		if d.Spec.ScreenSize.Width > MaxRDPScreenWidth || d.Spec.ScreenSize.Height > MaxRDPScreenHeight {
   197  			return trace.BadParameter("invalid screen size %dx%d (maximum %dx%d)",
   198  				d.Spec.ScreenSize.Width, d.Spec.ScreenSize.Height, MaxRDPScreenWidth, MaxRDPScreenHeight)
   199  		}
   200  	}
   201  
   202  	return nil
   203  }
   204  
   205  func (d *WindowsDesktopV3) GetScreenSize() (width, height uint32) {
   206  	if d.Spec.ScreenSize == nil {
   207  		return 0, 0
   208  	}
   209  	return d.Spec.ScreenSize.Width, d.Spec.ScreenSize.Height
   210  }
   211  
   212  // NonAD checks whether host is part of Active Directory
   213  func (d *WindowsDesktopV3) NonAD() bool {
   214  	return d.Spec.NonAD
   215  }
   216  
   217  // GetAddr returns the network address of this host.
   218  func (d *WindowsDesktopV3) GetAddr() string {
   219  	return d.Spec.Addr
   220  }
   221  
   222  // GetHostID returns the HostID for the associated desktop service.
   223  func (d *WindowsDesktopV3) GetHostID() string {
   224  	return d.Spec.HostID
   225  }
   226  
   227  // GetDomain returns the Active Directory domain of this host.
   228  func (d *WindowsDesktopV3) GetDomain() string {
   229  	return d.Spec.Domain
   230  }
   231  
   232  // MatchSearch goes through select field values and tries to
   233  // match against the list of search values.
   234  func (d *WindowsDesktopV3) MatchSearch(values []string) bool {
   235  	fieldVals := append(utils.MapToStrings(d.GetAllLabels()), d.GetName(), d.GetAddr())
   236  	return MatchSearch(fieldVals, values, nil)
   237  }
   238  
   239  // Copy returns a copy of this windows desktop object.
   240  func (d *WindowsDesktopV3) Copy() *WindowsDesktopV3 {
   241  	return utils.CloneProtoMsg(d)
   242  }
   243  
   244  func (d *WindowsDesktopV3) CloneResource() ResourceWithLabels {
   245  	return d.Copy()
   246  }
   247  
   248  // IsEqual determines if two windows desktop resources are equivalent to one another.
   249  func (d *WindowsDesktopV3) IsEqual(i WindowsDesktop) bool {
   250  	if other, ok := i.(*WindowsDesktopV3); ok {
   251  		return deriveTeleportEqualWindowsDesktopV3(d, other)
   252  	}
   253  	return false
   254  }
   255  
   256  // Match checks if a given desktop request matches this filter.
   257  func (f *WindowsDesktopFilter) Match(req WindowsDesktop) bool {
   258  	if f.HostID != "" && req.GetHostID() != f.HostID {
   259  		return false
   260  	}
   261  	if f.Name != "" && req.GetName() != f.Name {
   262  		return false
   263  	}
   264  	return true
   265  }
   266  
   267  // WindowsDesktops represents a list of Windows desktops.
   268  type WindowsDesktops []WindowsDesktop
   269  
   270  // Len returns the slice length.
   271  func (s WindowsDesktops) Len() int { return len(s) }
   272  
   273  // Less compares desktops by name and host ID.
   274  func (s WindowsDesktops) Less(i, j int) bool {
   275  	switch {
   276  	case s[i].GetName() < s[j].GetName():
   277  		return true
   278  	case s[i].GetName() > s[j].GetName():
   279  		return false
   280  	default:
   281  		return s[i].GetHostID() < s[j].GetHostID()
   282  	}
   283  }
   284  
   285  // Swap swaps two windows desktops.
   286  func (s WindowsDesktops) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   287  
   288  // SortByCustom custom sorts by given sort criteria.
   289  func (s WindowsDesktops) SortByCustom(sortBy SortBy) error {
   290  	if sortBy.Field == "" {
   291  		return nil
   292  	}
   293  
   294  	isDesc := sortBy.IsDesc
   295  	switch sortBy.Field {
   296  	case ResourceMetadataName:
   297  		sort.SliceStable(s, func(i, j int) bool {
   298  			return stringCompare(s[i].GetName(), s[j].GetName(), isDesc)
   299  		})
   300  	case ResourceSpecAddr:
   301  		sort.SliceStable(s, func(i, j int) bool {
   302  			return stringCompare(s[i].GetAddr(), s[j].GetAddr(), isDesc)
   303  		})
   304  	default:
   305  		return trace.NotImplemented("sorting by field %q for resource %q is not supported", sortBy.Field, KindWindowsDesktop)
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  // AsResources returns windows desktops as type resources with labels.
   312  func (s WindowsDesktops) AsResources() []ResourceWithLabels {
   313  	resources := make([]ResourceWithLabels, 0, len(s))
   314  	for _, server := range s {
   315  		resources = append(resources, ResourceWithLabels(server))
   316  	}
   317  	return resources
   318  }
   319  
   320  // GetFieldVals returns list of select field values.
   321  func (s WindowsDesktops) GetFieldVals(field string) ([]string, error) {
   322  	vals := make([]string, 0, len(s))
   323  	switch field {
   324  	case ResourceMetadataName:
   325  		for _, server := range s {
   326  			vals = append(vals, server.GetName())
   327  		}
   328  	case ResourceSpecAddr:
   329  		for _, server := range s {
   330  			vals = append(vals, server.GetAddr())
   331  		}
   332  	default:
   333  		return nil, trace.NotImplemented("getting field %q for resource %q is not supported", field, KindWindowsDesktop)
   334  	}
   335  
   336  	return vals, nil
   337  }
   338  
   339  // ListWindowsDesktopsResponse is a response type to ListWindowsDesktops.
   340  type ListWindowsDesktopsResponse struct {
   341  	Desktops []WindowsDesktop
   342  	NextKey  string
   343  }
   344  
   345  // ListWindowsDesktopsRequest is a request type to ListWindowsDesktops.
   346  type ListWindowsDesktopsRequest struct {
   347  	WindowsDesktopFilter
   348  	Limit                         int
   349  	StartKey, PredicateExpression string
   350  	Labels                        map[string]string
   351  	SearchKeywords                []string
   352  }
   353  
   354  // ListWindowsDesktopServicesResponse is a response type to ListWindowsDesktopServices.
   355  type ListWindowsDesktopServicesResponse struct {
   356  	DesktopServices []WindowsDesktopService
   357  	NextKey         string
   358  }
   359  
   360  // ListWindowsDesktopServicesRequest is a request type to ListWindowsDesktopServices.
   361  type ListWindowsDesktopServicesRequest struct {
   362  	Limit                         int
   363  	StartKey, PredicateExpression string
   364  	Labels                        map[string]string
   365  	SearchKeywords                []string
   366  }