Skip to content

Commit

Permalink
Create GET db and kube list web handlers (#6672)
Browse files Browse the repository at this point in the history
  • Loading branch information
kimlisa authored May 5, 2021
1 parent 697f76c commit 9910598
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 26 deletions.
8 changes: 7 additions & 1 deletion lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
h.GET("/webapi/sites/:site/namespaces/:namespace/nodes", h.WithClusterAuth(h.siteNodesGet))

// Get applications.
h.GET("/webapi/sites/:site/apps", h.WithClusterAuth(h.siteAppsGet))
h.GET("/webapi/sites/:site/apps", h.WithClusterAuth(h.clusterAppsGet))

// active sessions handlers
h.GET("/webapi/sites/:site/namespaces/:namespace/connect", h.WithClusterAuth(h.siteNodeConnect)) // connect to an active session (via websocket)
Expand All @@ -305,6 +305,12 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
// web context
h.GET("/webapi/sites/:site/context", h.WithClusterAuth(h.getUserContext))

// Database access handlers.
h.GET("/webapi/sites/:site/databases", h.WithClusterAuth(h.clusterDatabasesGet))

// Kube access handlers.
h.GET("/webapi/sites/:site/kubernetes", h.WithClusterAuth(h.clusterKubesGet))

// OIDC related callback handlers
h.GET("/webapi/oidc/login/web", h.WithRedirect(h.oidcLoginWeb))
h.GET("/webapi/oidc/callback", h.WithRedirect(h.oidcCallback))
Expand Down
83 changes: 83 additions & 0 deletions lib/web/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1805,6 +1805,89 @@ func (m *testModules) Features() modules.Features {
}
}

func TestClusterDatabasesGet(t *testing.T) {
env := newWebPack(t, 1)

proxy := env.proxies[0]
pack := proxy.authPack(t, "[email protected]")

endpoint := pack.clt.Endpoint("webapi", "sites", env.server.ClusterName(), "databases")
re, err := pack.clt.Get(context.Background(), endpoint, url.Values{})
require.NoError(t, err)

// No db registered.
dbs := []ui.Database{}
require.NoError(t, json.Unmarshal(re.Bytes(), &dbs))
require.Len(t, dbs, 0)

// Register a database.
db := types.NewDatabaseServerV3("test-db-name", map[string]string{"test-field": "test-value"}, types.DatabaseServerSpecV3{
Description: "test-description",
Protocol: "test-protocol",
URI: "test-uri",
Hostname: "test-hostname",
HostID: "test-hostID",
})

_, err = env.server.Auth().UpsertDatabaseServer(context.Background(), db)
require.NoError(t, err)

re, err = pack.clt.Get(context.Background(), endpoint, url.Values{})
require.NoError(t, err)

dbs = []ui.Database{}
require.NoError(t, json.Unmarshal(re.Bytes(), &dbs))
require.Len(t, dbs, 1)
require.EqualValues(t, ui.Database{
Name: "test-db-name",
Desc: "test-description",
Protocol: "test-protocol",
Type: types.DatabaseTypeSelfHosted,
Labels: []ui.Label{{Name: "test-field", Value: "test-value"}},
}, dbs[0])
}

func TestClusterKubesGet(t *testing.T) {
env := newWebPack(t, 1)

proxy := env.proxies[0]
pack := proxy.authPack(t, "[email protected]")

endpoint := pack.clt.Endpoint("webapi", "sites", env.server.ClusterName(), "kubernetes")
re, err := pack.clt.Get(context.Background(), endpoint, url.Values{})
require.NoError(t, err)

// No kube registered.
kbs := []ui.Kube{}
require.NoError(t, json.Unmarshal(re.Bytes(), &kbs))
require.Len(t, kbs, 0)

// Register a kube service.
err = env.server.Auth().UpsertKubeService(context.Background(), &services.ServerV2{
Metadata: services.Metadata{Name: "test-kube"},
Kind: services.KindKubeService,
Version: services.V2,
Spec: services.ServerSpecV2{
KubernetesClusters: []*services.KubernetesCluster{{
Name: "test-kube-name",
StaticLabels: map[string]string{"test-field": "test-value"},
}},
},
})
require.NoError(t, err)

re, err = pack.clt.Get(context.Background(), endpoint, url.Values{})
require.NoError(t, err)

kbs = []ui.Kube{}
require.NoError(t, json.Unmarshal(re.Bytes(), &kbs))
require.Len(t, kbs, 1)
require.EqualValues(t, ui.Kube{
Name: "test-kube-name",
Labels: []ui.Label{{Name: "test-field", Value: "test-value"}},
}, kbs[0])
}

// TestApplicationAccessDisabled makes sure application access can be disabled
// via modules.
func TestApplicationAccessDisabled(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions lib/web/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ import (
"github.com/julienschmidt/httprouter"
)

// siteAppsGet returns a list of applications in a form the UI can present.
func (h *Handler) siteAppsGet(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *SessionContext, site reversetunnel.RemoteSite) (interface{}, error) {
// clusterAppsGet returns a list of applications in a form the UI can present.
func (h *Handler) clusterAppsGet(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *SessionContext, site reversetunnel.RemoteSite) (interface{}, error) {
appClusterName := p.ByName("site")

// Get a list of application servers.
Expand Down
59 changes: 59 additions & 0 deletions lib/web/servers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* 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 web

import (
"net/http"

"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/web/ui"
"github.com/gravitational/trace"
"github.com/julienschmidt/httprouter"
)

// clusterKubesGet returns a list of kube clusters in a form the UI can present.
func (h *Handler) clusterKubesGet(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *SessionContext, site reversetunnel.RemoteSite) (interface{}, error) {
clt, err := ctx.GetUserClient(site)
if err != nil {
return nil, trace.Wrap(err)
}

// Get a list of kube servers.
kubeServers, err := clt.GetKubeServices(r.Context())
if err != nil {
return nil, trace.Wrap(err)
}

return ui.MakeKubes(h.auth.clusterName, kubeServers), nil
}

// clusterDatabasesGet returns a list of db servers in a form the UI can present.
func (h *Handler) clusterDatabasesGet(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *SessionContext, site reversetunnel.RemoteSite) (interface{}, error) {
clt, err := ctx.GetUserClient(site)
if err != nil {
return nil, trace.Wrap(err)
}

// Get a list of database servers.
dbServers, err := clt.GetDatabaseServers(r.Context(), defaults.Namespace)
if err != nil {
return nil, trace.Wrap(err)
}

return ui.MakeDatabases(h.auth.clusterName, dbServers), nil
}
18 changes: 7 additions & 11 deletions lib/web/ui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,11 @@ package ui

import (
"fmt"
"sort"

"github.com/gravitational/teleport/lib/services"
)

// AppLabel describes an application label
type AppLabel struct {
// Name is this label name
Name string `json:"name"`
// Value is this label value
Value string `json:"value"`
}

// App describes an application
type App struct {
// Name is the name of the application.
Expand All @@ -43,7 +36,7 @@ type App struct {
// ClusterID is this app cluster ID
ClusterID string `json:"clusterId"`
// Labels is a map of static labels associated with an application.
Labels []AppLabel `json:"labels"`
Labels []Label `json:"labels"`
}

// MakeApps creates server application objects
Expand All @@ -53,13 +46,16 @@ func MakeApps(localClusterName string, localProxyDNSName string, appClusterName
teleApps := server.GetApps()
for _, teleApp := range teleApps {
fqdn := AssembleAppFQDN(localClusterName, localProxyDNSName, appClusterName, teleApp)
labels := []AppLabel{}
labels := []Label{}
for name, value := range teleApp.StaticLabels {
labels = append(labels, AppLabel{
labels = append(labels, Label{
Name: name,
Value: value,
})
}

sort.Sort(sortedLabels(labels))

result = append(result, App{
Name: teleApp.Name,
URI: teleApp.URI,
Expand Down
91 changes: 91 additions & 0 deletions lib/web/ui/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package ui
import (
"sort"

"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/services"
)

Expand Down Expand Up @@ -96,3 +97,93 @@ func MakeServers(clusterName string, servers []services.Server) []Server {

return uiServers
}

// Kube describes a kube cluster.
type Kube struct {
// Name is the name of the kube cluster.
Name string `json:"name"`
// Labels is a map of static and dynamic labels associated with an kube cluster.
Labels []Label `json:"labels"`
}

// MakeKubes creates ui kube objects and returns a list..
func MakeKubes(clusterName string, servers []types.Server) []Kube {
uiKubeClusters := []Kube{}
for _, server := range servers {
// Process each kube cluster.
for _, cluster := range server.GetKubernetesClusters() {
uiLabels := []Label{}

for name, value := range cluster.StaticLabels {
uiLabels = append(uiLabels, Label{
Name: name,
Value: value,
})
}

for name, cmd := range cluster.DynamicLabels {
uiLabels = append(uiLabels, Label{
Name: name,
Value: cmd.GetResult(),
})
}

sort.Sort(sortedLabels(uiLabels))

uiKubeClusters = append(uiKubeClusters, Kube{
Name: cluster.Name,
Labels: uiLabels,
})
}
}

return uiKubeClusters
}

// Database describes a database server.
type Database struct {
// Name is the name of the database.
Name string `json:"name"`
// Desc is the database description.
Desc string `json:"desc"`
// Protocol is the database description.
Protocol string `json:"protocol"`
// Type is the database type, self-hosted or cloud-hosted.
Type string `json:"type"`
// Labels is a map of static and dynamic labels associated with an database.
Labels []Label `json:"labels"`
}

// MakeDatabases creates server database objects.
func MakeDatabases(clusterName string, servers []types.DatabaseServer) []Database {
uiServers := make([]Database, 0, len(servers))
for _, server := range servers {
uiLabels := []Label{}

for name, value := range server.GetStaticLabels() {
uiLabels = append(uiLabels, Label{
Name: name,
Value: value,
})
}

for name, cmd := range server.GetDynamicLabels() {
uiLabels = append(uiLabels, Label{
Name: name,
Value: cmd.GetResult(),
})
}

sort.Sort(sortedLabels(uiLabels))

uiServers = append(uiServers, Database{
Name: server.GetName(),
Desc: server.GetDescription(),
Protocol: server.GetProtocol(),
Type: server.GetType(),
Labels: uiLabels,
})
}

return uiServers
}
33 changes: 21 additions & 12 deletions lib/web/ui/usercontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package ui

import (
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
Expand Down Expand Up @@ -66,6 +67,10 @@ type userACL struct {
Nodes access `json:"nodes"`
// AppServers defines access to application servers
AppServers access `json:"appServers"`
// DBServers defines access to database servers.
DBServers access `json:"dbServers"`
// KubeServers defines access to kubernetes servers.
KubeServers access `json:"kubeServers"`
// SSH defines access to servers
SSHLogins []string `json:"sshLogins"`
// AccessRequests defines access to access requests
Expand Down Expand Up @@ -166,26 +171,30 @@ func getAccessStrategy(roleset services.RoleSet) accessStrategy {
}

// NewUserContext returns user context
func NewUserContext(user services.User, userRoles services.RoleSet) (*UserContext, error) {
func NewUserContext(user types.User, userRoles services.RoleSet) (*UserContext, error) {
ctx := &services.Context{User: user}
sessionAccess := newAccess(userRoles, ctx, services.KindSession)
roleAccess := newAccess(userRoles, ctx, services.KindRole)
authConnectors := newAccess(userRoles, ctx, services.KindAuthConnector)
trustedClusterAccess := newAccess(userRoles, ctx, services.KindTrustedCluster)
eventAccess := newAccess(userRoles, ctx, services.KindEvent)
userAccess := newAccess(userRoles, ctx, services.KindUser)
tokenAccess := newAccess(userRoles, ctx, services.KindToken)
nodeAccess := newAccess(userRoles, ctx, services.KindNode)
appServerAccess := newAccess(userRoles, ctx, services.KindAppServer)
requestAccess := newAccess(userRoles, ctx, services.KindAccessRequest)
billingAccess := newAccess(userRoles, ctx, services.KindBilling)
sessionAccess := newAccess(userRoles, ctx, types.KindSession)
roleAccess := newAccess(userRoles, ctx, types.KindRole)
authConnectors := newAccess(userRoles, ctx, types.KindAuthConnector)
trustedClusterAccess := newAccess(userRoles, ctx, types.KindTrustedCluster)
eventAccess := newAccess(userRoles, ctx, types.KindEvent)
userAccess := newAccess(userRoles, ctx, types.KindUser)
tokenAccess := newAccess(userRoles, ctx, types.KindToken)
nodeAccess := newAccess(userRoles, ctx, types.KindNode)
appServerAccess := newAccess(userRoles, ctx, types.KindAppServer)
dbServerAccess := newAccess(userRoles, ctx, types.KindDatabaseServer)
kubeServerAccess := newAccess(userRoles, ctx, types.KindKubeService)
requestAccess := newAccess(userRoles, ctx, types.KindAccessRequest)
billingAccess := newAccess(userRoles, ctx, types.KindBilling)

logins := getLogins(userRoles)
accessStrategy := getAccessStrategy(userRoles)

acl := userACL{
AccessRequests: requestAccess,
AppServers: appServerAccess,
DBServers: dbServerAccess,
KubeServers: kubeServerAccess,
AuthConnectors: authConnectors,
TrustedClusters: trustedClusterAccess,
Sessions: sessionAccess,
Expand Down
Loading

0 comments on commit 9910598

Please sign in to comment.