2021-02-22 16:54:36 +00:00
|
|
|
/*
|
|
|
|
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 (
|
|
|
|
"context"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
|
|
"github.com/gravitational/teleport/lib/httplib"
|
|
|
|
"github.com/gravitational/teleport/lib/services"
|
|
|
|
"github.com/gravitational/teleport/lib/web/ui"
|
|
|
|
|
|
|
|
"github.com/gravitational/trace"
|
|
|
|
|
|
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
kyaml "k8s.io/apimachinery/pkg/util/yaml"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (h *Handler) getRolesHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return getRoles(clt)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getRoles(clt resourcesAPIGetter) ([]ui.ResourceItem, error) {
|
2021-03-11 01:54:08 +00:00
|
|
|
roles, err := clt.GetRoles(context.TODO())
|
2021-02-22 16:54:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ui.NewRoles(roles)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) deleteRole(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
roleName := params.ByName("name")
|
|
|
|
if err := clt.DeleteRole(r.Context(), roleName); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) upsertRoleHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var req ui.ResourceItem
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return upsertRole(r.Context(), clt, req.Content, r.Method)
|
|
|
|
}
|
|
|
|
|
|
|
|
func upsertRole(ctx context.Context, clt resourcesAPIGetter, content, httpMethod string) (*ui.ResourceItem, error) {
|
|
|
|
extractedRes, err := ExtractResourceAndValidate(content)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if extractedRes.Kind != types.KindRole {
|
|
|
|
return nil, trace.BadParameter("resource kind %q is invalid", extractedRes.Kind)
|
|
|
|
}
|
|
|
|
|
2021-03-11 01:54:08 +00:00
|
|
|
_, err = clt.GetRole(ctx, extractedRes.Metadata.Name)
|
2021-02-22 16:54:36 +00:00
|
|
|
if err := CheckResourceUpsertableByError(err, httpMethod, extractedRes.Metadata.Name); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
role, err := services.UnmarshalRole(extractedRes.Raw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := clt.UpsertRole(ctx, role); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ui.NewResourceItem(role)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) getGithubConnectorsHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2021-03-24 01:26:52 +00:00
|
|
|
return getGithubConnectors(r.Context(), clt)
|
2021-02-22 16:54:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 01:26:52 +00:00
|
|
|
func getGithubConnectors(ctx context.Context, clt resourcesAPIGetter) ([]ui.ResourceItem, error) {
|
|
|
|
connectors, err := clt.GetGithubConnectors(ctx, true)
|
2021-02-22 16:54:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ui.NewGithubConnectors(connectors)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) deleteGithubConnector(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
connectorName := params.ByName("name")
|
|
|
|
if err := clt.DeleteGithubConnector(r.Context(), connectorName); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) upsertGithubConnectorHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var req ui.ResourceItem
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return upsertGithubConnector(r.Context(), clt, req.Content, r.Method)
|
|
|
|
}
|
|
|
|
|
|
|
|
func upsertGithubConnector(ctx context.Context, clt resourcesAPIGetter, content, httpMethod string) (*ui.ResourceItem, error) {
|
|
|
|
extractedRes, err := ExtractResourceAndValidate(content)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if extractedRes.Kind != types.KindGithubConnector {
|
|
|
|
return nil, trace.BadParameter("resource kind %q is invalid", extractedRes.Kind)
|
|
|
|
}
|
|
|
|
|
2021-03-24 01:26:52 +00:00
|
|
|
_, err = clt.GetGithubConnector(ctx, extractedRes.Metadata.Name, false)
|
2021-02-22 16:54:36 +00:00
|
|
|
if err := CheckResourceUpsertableByError(err, httpMethod, extractedRes.Metadata.Name); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
connector, err := services.UnmarshalGithubConnector(extractedRes.Raw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := clt.UpsertGithubConnector(ctx, connector); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ui.NewResourceItem(connector)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) getTrustedClustersHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2021-03-24 01:26:52 +00:00
|
|
|
return getTrustedClusters(r.Context(), clt)
|
2021-02-22 16:54:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 01:26:52 +00:00
|
|
|
func getTrustedClusters(ctx context.Context, clt resourcesAPIGetter) ([]ui.ResourceItem, error) {
|
|
|
|
trustedClusters, err := clt.GetTrustedClusters(ctx)
|
2021-02-22 16:54:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ui.NewTrustedClusters(trustedClusters)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) deleteTrustedCluster(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tcName := params.ByName("name")
|
|
|
|
if err := clt.DeleteTrustedCluster(r.Context(), tcName); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Handler) upsertTrustedClusterHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
|
|
|
|
clt, err := ctx.GetClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var req ui.ResourceItem
|
|
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return upsertTrustedCluster(r.Context(), clt, req.Content, r.Method)
|
|
|
|
}
|
|
|
|
|
|
|
|
func upsertTrustedCluster(ctx context.Context, clt resourcesAPIGetter, content, httpMethod string) (*ui.ResourceItem, error) {
|
|
|
|
extractedRes, err := ExtractResourceAndValidate(content)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if extractedRes.Kind != types.KindTrustedCluster {
|
|
|
|
return nil, trace.BadParameter("resource kind %q is invalid", extractedRes.Kind)
|
|
|
|
}
|
|
|
|
|
2021-03-24 01:26:52 +00:00
|
|
|
_, err = clt.GetTrustedCluster(ctx, extractedRes.Metadata.Name)
|
2021-02-22 16:54:36 +00:00
|
|
|
if err := CheckResourceUpsertableByError(err, httpMethod, extractedRes.Metadata.Name); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tc, err := services.UnmarshalTrustedCluster(extractedRes.Raw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = clt.UpsertTrustedCluster(ctx, tc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return ui.NewResourceItem(tc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CheckResourceUpsertableByError checks if the resource is upsertable by the state of error with
|
|
|
|
// the request http method used.
|
|
|
|
func CheckResourceUpsertableByError(err error, httpMethod, resourceName string) error {
|
|
|
|
if err != nil && !trace.IsNotFound(err) {
|
|
|
|
return trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
exists := err == nil
|
|
|
|
if exists && httpMethod == http.MethodPost {
|
|
|
|
return trace.AlreadyExists("resource name %q already exists", resourceName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exists && httpMethod == http.MethodPut {
|
|
|
|
return trace.NotFound("cannot find resource with name %q", resourceName)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExtractResourceAndValidate extracts resource information from given string and validates basic fields.
|
|
|
|
func ExtractResourceAndValidate(yaml string) (*services.UnknownResource, error) {
|
|
|
|
var unknownRes services.UnknownResource
|
|
|
|
reader := strings.NewReader(yaml)
|
|
|
|
decoder := kyaml.NewYAMLOrJSONDecoder(reader, 32*1024)
|
|
|
|
|
|
|
|
if err := decoder.Decode(&unknownRes); err != nil {
|
|
|
|
return nil, trace.BadParameter("not a valid resource declaration")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := unknownRes.Metadata.CheckAndSetDefaults(); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &unknownRes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type resourcesAPIGetter interface {
|
|
|
|
// GetRole returns role by name
|
2021-03-11 01:54:08 +00:00
|
|
|
GetRole(ctx context.Context, name string) (types.Role, error)
|
2021-02-22 16:54:36 +00:00
|
|
|
// GetRoles returns a list of roles
|
2021-03-11 01:54:08 +00:00
|
|
|
GetRoles(ctx context.Context) ([]types.Role, error)
|
2021-02-22 16:54:36 +00:00
|
|
|
// UpsertRole creates or updates role
|
|
|
|
UpsertRole(ctx context.Context, role types.Role) error
|
|
|
|
// UpsertGithubConnector creates or updates a Github connector
|
|
|
|
UpsertGithubConnector(ctx context.Context, connector types.GithubConnector) error
|
|
|
|
// GetGithubConnectors returns all configured Github connectors
|
2021-03-24 01:26:52 +00:00
|
|
|
GetGithubConnectors(ctx context.Context, withSecrets bool) ([]types.GithubConnector, error)
|
2021-02-22 16:54:36 +00:00
|
|
|
// GetGithubConnector returns the specified Github connector
|
2021-03-24 01:26:52 +00:00
|
|
|
GetGithubConnector(ctx context.Context, id string, withSecrets bool) (types.GithubConnector, error)
|
2021-02-22 16:54:36 +00:00
|
|
|
// DeleteGithubConnector deletes the specified Github connector
|
|
|
|
DeleteGithubConnector(ctx context.Context, id string) error
|
|
|
|
// UpsertTrustedCluster creates or updates a TrustedCluster in the backend.
|
|
|
|
UpsertTrustedCluster(ctx context.Context, tc types.TrustedCluster) (types.TrustedCluster, error)
|
|
|
|
// GetTrustedCluster returns a single TrustedCluster by name.
|
2021-03-24 01:26:52 +00:00
|
|
|
GetTrustedCluster(ctx context.Context, name string) (types.TrustedCluster, error)
|
2021-02-22 16:54:36 +00:00
|
|
|
// GetTrustedClusters returns all TrustedClusters in the backend.
|
2021-03-24 01:26:52 +00:00
|
|
|
GetTrustedClusters(ctx context.Context) ([]types.TrustedCluster, error)
|
2021-02-22 16:54:36 +00:00
|
|
|
// DeleteTrustedCluster removes a TrustedCluster from the backend by name.
|
|
|
|
DeleteTrustedCluster(ctx context.Context, name string) error
|
|
|
|
}
|