blob: b9c68c3148c646deef5e1ec725b64ee86efa7c7b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 buildin
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-chassis/cari/discovery"
rbacmodel "github.com/go-chassis/cari/rbac"
"github.com/apache/servicecomb-service-center/datasource"
"github.com/apache/servicecomb-service-center/pkg/rest"
"github.com/apache/servicecomb-service-center/server/plugin/auth"
"github.com/apache/servicecomb-service-center/server/service/rbac"
)
const (
LabelEnvironment = "environment"
LabelAppID = "appId"
LabelServiceName = "serviceName"
QueryEnv = "env"
)
var (
// APIServiceExistence Apply by service key or serviceId
// - /v4/:project/registry/existence?env=xxx&appId=xxx&serviceName=xxx
// - /v4/:project/registry/existence?serviceId=xxx&schemaId=xxx
APIServiceExistence = "/v4/:project/registry/existence"
// APIServicesList Method GET: apply all by optional service key
// Method POST or DELETE: apply by request body
APIServicesList = "/v4/:project/registry/microservices"
APIServiceInfo = "/v4/:project/registry/microservices/:serviceId"
APIProConDependency = "/v4/:project/registry/microservices/:providerId/consumers"
APIConProDependency = "/v4/:project/registry/microservices/:consumerId/providers"
// APIDiscovery Apply by service key
APIDiscovery = "/v4/:project/registry/instances"
// APIBatchDiscovery Apply by request body
APIBatchDiscovery = "/v4/:project/registry/instances/action"
// APIHeartbeats Apply by request body
APIHeartbeats = "/v4/:project/registry/heartbeats"
// APIGovServicesList Apply by optional service key
// - /v4/:project/govern/microservices?appId=xxx
// Apply all:
// - /v4/:project/govern/microservices?options=statistics
// - /v4/:project/govern/microservices/statistics
APIGovServicesList = "/v4/:project/govern/microservices"
APIGovServiceInfo = "/v4/:project/govern/microservices/:serviceId"
)
func init() {
RegisterParseFunc(APIServiceInfo, ByServiceID)
RegisterParseFunc(APIGovServiceInfo, ByServiceID)
RegisterParseFunc(APIProConDependency, func(r *http.Request) (*auth.ResourceScope, error) {
return fromQueryKey(r, ":providerId")
})
RegisterParseFunc(APIConProDependency, func(r *http.Request) (*auth.ResourceScope, error) {
return fromQueryKey(r, ":consumerId")
})
RegisterParseFunc(APIDiscovery, ByServiceKey)
RegisterParseFunc(APIServiceExistence, ByServiceKey)
RegisterParseFunc(APIGovServicesList, ApplyAll)
RegisterParseFunc(APIServicesList, ByRequestBody)
RegisterParseFunc(APIBatchDiscovery, ByDiscoveryRequestBody)
RegisterParseFunc(APIHeartbeats, ByHeartbeatRequestBody)
}
func ByServiceID(r *http.Request) (*auth.ResourceScope, error) {
return fromQueryKey(r, ":serviceId")
}
func fromQueryKey(r *http.Request, queryKey string) (*auth.ResourceScope, error) {
ctx := r.Context()
apiPath, ok := ctx.Value(rest.CtxMatchPattern).(string)
if !ok {
return nil, ErrCtxMatchPatternNotFound
}
serviceID := r.URL.Query().Get(queryKey)
labels, err := serviceIDToLabels(ctx, serviceID)
if err != nil {
return nil, err
}
return &auth.ResourceScope{
Type: rbacmodel.GetResource(apiPath),
Labels: labels,
Verb: rbac.MethodToVerbs[r.Method],
}, nil
}
func serviceIDToLabels(ctx context.Context, serviceID string) ([]map[string]string, error) {
service, err := datasource.GetMetadataManager().GetService(ctx, &discovery.GetServiceRequest{ServiceId: serviceID})
if err != nil {
return nil, err
}
return []map[string]string{{
LabelEnvironment: service.Environment,
LabelAppID: service.AppId,
LabelServiceName: service.ServiceName,
}}, nil
}
func ByServiceKey(r *http.Request) (*auth.ResourceScope, error) {
query := r.URL.Query()
if _, ok := query["serviceId"]; ok {
return fromQueryKey(r, "serviceId")
}
apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
if !ok {
return nil, ErrCtxMatchPatternNotFound
}
return &auth.ResourceScope{
Type: rbacmodel.GetResource(apiPath),
Labels: []map[string]string{{
LabelEnvironment: query.Get(QueryEnv),
LabelAppID: query.Get(LabelAppID),
LabelServiceName: query.Get(LabelServiceName),
}},
Verb: rbac.MethodToVerbs[r.Method],
}, nil
}
func ByRequestBody(r *http.Request) (*auth.ResourceScope, error) {
if r.Method == http.MethodGet {
// get or list by query string
return ApplyAll(r)
}
return fromRequestBody(r)
}
func fromRequestBody(r *http.Request) (*auth.ResourceScope, error) {
apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
if !ok {
return nil, ErrCtxMatchPatternNotFound
}
var (
labels []map[string]string
err error
)
if r.Method == http.MethodDelete {
// batch delete
labels, err = deleteServicesToLabels(r)
} else {
// create service
labels, err = createServiceToLabels(r)
}
if err != nil {
return nil, err
}
return &auth.ResourceScope{
Type: rbacmodel.GetResource(apiPath),
Labels: labels,
Verb: rbac.MethodToVerbs[r.Method],
}, nil
}
func createServiceToLabels(r *http.Request) ([]map[string]string, error) {
message, err := rest.ReadBody(r)
if err != nil {
return nil, err
}
request := &discovery.CreateServiceRequest{}
err = json.Unmarshal(message, request)
if err != nil {
return nil, err
}
service := request.Service
if service == nil {
return nil, fmt.Errorf("invalid CreateServiceRequest")
}
return []map[string]string{{
LabelEnvironment: service.Environment,
LabelAppID: service.AppId,
LabelServiceName: service.ServiceName,
}}, nil
}
func deleteServicesToLabels(r *http.Request) ([]map[string]string, error) {
message, err := rest.ReadBody(r)
if err != nil {
return nil, err
}
request := &discovery.DelServicesRequest{}
err = json.Unmarshal(message, request)
if err != nil {
return nil, err
}
ctx := r.Context()
var labels []map[string]string
for _, serviceID := range request.ServiceIds {
ls, err := serviceIDToLabels(ctx, serviceID)
if err != nil {
return nil, err
}
labels = append(labels, ls...)
}
return labels, nil
}
func ByDiscoveryRequestBody(r *http.Request) (*auth.ResourceScope, error) {
apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
if !ok {
return nil, ErrCtxMatchPatternNotFound
}
message, err := rest.ReadBody(r)
if err != nil {
return nil, err
}
request := &discovery.BatchFindInstancesRequest{}
err = json.Unmarshal(message, request)
if err != nil {
return nil, err
}
ctx := r.Context()
var labels []map[string]string
for _, it := range request.Instances {
ls, err := serviceIDToLabels(ctx, it.Instance.ServiceId)
if err != nil {
return nil, err
}
labels = append(labels, ls...)
}
return &auth.ResourceScope{
Type: rbacmodel.GetResource(apiPath),
Labels: labels,
Verb: "get",
}, nil
}
func ByHeartbeatRequestBody(r *http.Request) (*auth.ResourceScope, error) {
apiPath, ok := r.Context().Value(rest.CtxMatchPattern).(string)
if !ok {
return nil, ErrCtxMatchPatternNotFound
}
message, err := rest.ReadBody(r)
if err != nil {
return nil, err
}
request := &discovery.HeartbeatSetRequest{}
err = json.Unmarshal(message, request)
if err != nil {
return nil, err
}
ctx := r.Context()
var labels []map[string]string
for _, instance := range request.Instances {
ls, err := serviceIDToLabels(ctx, instance.ServiceId)
if err != nil {
return nil, err
}
labels = append(labels, ls...)
}
return &auth.ResourceScope{
Type: rbacmodel.GetResource(apiPath),
Labels: labels,
Verb: "update",
}, nil
}