-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
cluster.go
189 lines (161 loc) · 5.21 KB
/
cluster.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
Copyright 2021 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 clusters
import (
"context"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/teleterm/api/uri"
)
// Cluster describes user settings and access to various resources.
type Cluster struct {
// URI is the cluster URI
URI uri.ResourceURI
// Name is the cluster name
Name string
// ProfileName is the name of the tsh profile
ProfileName string
// Log is a component logger
Log *logrus.Entry
// dir is the directory where cluster certificates are stored
dir string
// Status is the cluster status
status client.ProfileStatus
// client is the cluster Teleport client
clusterClient *client.TeleportClient
// clock is a clock for time-related operations
clock clockwork.Clock
// Auth server features
// only present where the auth client can be queried
// and set with GetClusterFeatures
Features *proto.Features
}
// Connected indicates if connection to the cluster can be established
func (c *Cluster) Connected() bool {
return c.status.Name != "" && !c.status.IsExpired(c.clock)
}
// GetClusterFeatures returns a list of features enabled/disabled by the auth server
func (c *Cluster) GetClusterFeatures(ctx context.Context) (*proto.Features, error) {
var authPingResponse proto.PingResponse
err := addMetadataToRetryableError(ctx, func() error {
proxyClient, err := c.clusterClient.ConnectToProxy(ctx)
if err != nil {
return trace.Wrap(err)
}
defer proxyClient.Close()
authPingResponse, err = proxyClient.CurrentCluster().Ping(ctx)
return trace.Wrap(err)
})
if err != nil {
return nil, trace.Wrap(err)
}
return authPingResponse.ServerFeatures, nil
}
// GetRoles returns currently logged-in user roles
func (c *Cluster) GetRoles(ctx context.Context) ([]*types.Role, error) {
var roles []*types.Role
err := addMetadataToRetryableError(ctx, func() error {
proxyClient, err := c.clusterClient.ConnectToProxy(ctx)
if err != nil {
return trace.Wrap(err)
}
defer proxyClient.Close()
for _, name := range c.status.Roles {
role, err := proxyClient.GetRole(ctx, name)
if err != nil {
return trace.Wrap(err)
}
roles = append(roles, &role)
}
return nil
})
if err != nil {
return nil, trace.Wrap(err)
}
return roles, nil
}
// GetRequestableRoles returns the requestable roles for the currently logged-in user
func (c *Cluster) GetRequestableRoles(ctx context.Context) ([]string, error) {
var (
authClient auth.ClientI
proxyClient *client.ProxyClient
err error
results []string
)
err = addMetadataToRetryableError(ctx, func() error {
proxyClient, err = c.clusterClient.ConnectToProxy(ctx)
if err != nil {
return trace.Wrap(err)
}
defer proxyClient.Close()
authClient, err = proxyClient.ConnectToCluster(ctx, c.clusterClient.SiteName)
if err != nil {
return trace.Wrap(err)
}
defer authClient.Close()
response, err := authClient.GetAccessCapabilities(ctx, types.AccessCapabilitiesRequest{
RequestableRoles: true,
})
if err != nil {
return trace.Wrap(err)
}
results = append(results, response.RequestableRoles...)
return nil
})
return results, nil
}
// GetLoggedInUser returns currently logged-in user
func (c *Cluster) GetLoggedInUser() LoggedInUser {
return LoggedInUser{
Name: c.status.Username,
SSHLogins: c.status.Logins,
Roles: c.status.Roles,
ActiveRequests: c.status.ActiveRequests.AccessRequests,
}
}
// GetProxyHost returns proxy address (host:port) of the cluster
func (c *Cluster) GetProxyHost() string {
return c.status.ProxyURL.Host
}
// LoggedInUser is the currently logged-in user
type LoggedInUser struct {
// Name is the user name
Name string
// SSHLogins is the user sshlogins
SSHLogins []string
// Roles is the user roles
Roles []string
// ActiveRequests is the user active requests
ActiveRequests []string
}
// addMetadataToRetryableError is Connect's equivalent of client.RetryWithRelogin. By adding the
// metadata to the error, we're letting the Electron app know that the given error was caused by
// expired certs and letting the user log in again should resolve the error upon another attempt.
func addMetadataToRetryableError(ctx context.Context, fn func() error) error {
err := fn()
if err == nil {
return nil
}
if client.IsErrorResolvableWithRelogin(err) {
trailer := metadata.Pairs("is-resolvable-with-relogin", "1")
grpc.SetTrailer(ctx, trailer)
}
return trace.Wrap(err)
}