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 }