mirror of
https://github.com/openziti/ziti.git
synced 2026-09-10 00:35:41 +00:00
86092a8640
Bumps the sdk-golang dependency from v1 to the v2 module (`github.com/openziti/sdk-golang/v2` at v2.0.0-pre1) and updates all import paths. This is a no-behavior-change precursor that isolates the dependency migration from the Connect-V2 feature work in #3884. - Rewrites `github.com/openziti/sdk-golang/...` imports to `github.com/openziti/sdk-golang/v2/...` across the main and zititest modules. - Pins both modules to `github.com/openziti/sdk-golang/v2 v2.0.0-pre1`. - Adapts `edgeXgressConn.AcceptMessage` to the v2 `MsgSink` signature, which now takes an `edge.SdkChannel` argument. - Replaces the removed `edge.Conn.GetRouterId()` with `RemoteAddr().String()` in the loop4 traffic-test logging. For openziti/sdk-golang#936.
1032 lines
30 KiB
Go
1032 lines
30 KiB
Go
/*
|
|
Copyright NetFoundry 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
|
|
|
|
https://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 tests
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"math/big"
|
|
"reflect"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/Jeffail/gabs"
|
|
"github.com/google/uuid"
|
|
"github.com/openziti/edge-api/rest_model"
|
|
"github.com/openziti/sdk-golang/v2/ziti"
|
|
"github.com/openziti/ziti/v2/common/eid"
|
|
)
|
|
|
|
// derefOrNil returns the dereferenced value of a pointer as an interface{},
|
|
// or nil if the pointer is nil. Useful for passing *string values to
|
|
// pathEquals and setValue which expect interface{}.
|
|
func derefOrNil[T any](p *T) interface{} {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
return *p
|
|
}
|
|
|
|
type entity interface {
|
|
getId() string
|
|
setId(string)
|
|
getEntityType() string
|
|
toJson(create bool, ctx *TestContext, fields ...string) string
|
|
validate(ctx *TestContext, c *gabs.Container)
|
|
}
|
|
|
|
type loadableEntity interface {
|
|
entity
|
|
fromJson(ctx *TestContext, c *gabs.Container)
|
|
}
|
|
|
|
type postureCheck struct {
|
|
id string
|
|
name string
|
|
typeId string
|
|
roleAttributes []string
|
|
tags map[string]interface{}
|
|
}
|
|
|
|
func (p *postureCheck) getId() string {
|
|
return p.id
|
|
}
|
|
|
|
func (p *postureCheck) setId(id string) {
|
|
p.id = id
|
|
}
|
|
|
|
func (p *postureCheck) getEntityType() string {
|
|
return "posture-checks"
|
|
}
|
|
|
|
func (p *postureCheck) toJson(create bool, ctx *TestContext, fields ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, p.name, "name")
|
|
ctx.setJsonValue(entityData, p.roleAttributes, "roleAttributes")
|
|
ctx.setJsonValue(entityData, p.typeId, "typeId")
|
|
|
|
if len(p.tags) > 0 {
|
|
ctx.setJsonValue(entityData, p.tags, "tags")
|
|
}
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (p postureCheck) validate(ctx *TestContext, c *gabs.Container) {}
|
|
|
|
type postureCheckDomain struct {
|
|
postureCheck
|
|
domains []string
|
|
}
|
|
|
|
func (entity *postureCheckDomain) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *postureCheckDomain) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *postureCheckDomain) getEntityType() string {
|
|
return "posture-checks"
|
|
}
|
|
|
|
func (entity *postureCheckDomain) toJson(_ bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.name, "name")
|
|
ctx.setJsonValue(entityData, entity.domains, "domains")
|
|
ctx.setJsonValue(entityData, entity.roleAttributes, "roleAttributes")
|
|
ctx.setJsonValue(entityData, entity.typeId, "typeId")
|
|
|
|
if len(entity.tags) > 0 {
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
}
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *postureCheckDomain) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
|
|
sort.Strings(entity.domains)
|
|
ctx.pathEqualsStringSlice(c, entity.domains, path("domains"))
|
|
|
|
sort.Strings(entity.roleAttributes)
|
|
ctx.pathEqualsStringSlice(c, entity.roleAttributes, path("roleAttributes"))
|
|
}
|
|
|
|
type service struct {
|
|
Id string
|
|
Name string
|
|
terminatorStrategy string
|
|
roleAttributes []string
|
|
configs []string
|
|
permissions []string
|
|
tags map[string]interface{}
|
|
encryptionRequired bool
|
|
}
|
|
|
|
func (entity *service) getId() string {
|
|
return entity.Id
|
|
}
|
|
|
|
func (entity *service) setId(id string) {
|
|
entity.Id = id
|
|
}
|
|
|
|
func (entity *service) getEntityType() string {
|
|
return "services"
|
|
}
|
|
|
|
func (entity *service) toJson(_ bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.Name, "name")
|
|
ctx.setJsonValue(entityData, entity.terminatorStrategy, "terminatorStrategy")
|
|
ctx.setJsonValue(entityData, entity.roleAttributes, "roleAttributes")
|
|
ctx.setJsonValue(entityData, entity.configs, "configs")
|
|
ctx.setJsonValue(entityData, entity.encryptionRequired, "encryptionRequired")
|
|
|
|
if len(entity.tags) > 0 {
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
}
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *service) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.Name, path("name"))
|
|
ctx.pathEquals(c, entity.terminatorStrategy, path("terminatorStrategy"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
|
|
sort.Strings(entity.roleAttributes)
|
|
ctx.pathEqualsStringSlice(c, entity.roleAttributes, path("roleAttributes"))
|
|
|
|
sort.Strings(entity.permissions)
|
|
ctx.pathEqualsStringSlice(c, entity.permissions, path("permissions"))
|
|
}
|
|
|
|
type terminator struct {
|
|
id string
|
|
serviceId string
|
|
routerId string
|
|
binding string
|
|
address string
|
|
cost int
|
|
precedence string
|
|
tags map[string]interface{}
|
|
}
|
|
|
|
func (entity *terminator) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *terminator) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *terminator) getEntityType() string {
|
|
return "terminators"
|
|
}
|
|
|
|
func (entity *terminator) toJson(_ bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.serviceId, "service")
|
|
ctx.setJsonValue(entityData, entity.routerId, "router")
|
|
ctx.setJsonValue(entityData, entity.binding, "binding")
|
|
ctx.setJsonValue(entityData, entity.address, "address")
|
|
|
|
if len(entity.tags) > 0 {
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
}
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *terminator) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.serviceId, path("serviceId"))
|
|
ctx.pathEquals(c, entity.routerId, path("routerId"))
|
|
ctx.pathEquals(c, entity.binding, path("binding"))
|
|
ctx.pathEquals(c, entity.address, path("address"))
|
|
ctx.pathEquals(c, float64(entity.cost), path("cost"))
|
|
ctx.pathEquals(c, entity.precedence, path("precedence"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
}
|
|
|
|
func (entity *terminator) fromJson(ctx *TestContext, c *gabs.Container) {
|
|
entity.id = ctx.requireString(c, "id")
|
|
entity.serviceId = ctx.requireString(c, "serviceId")
|
|
entity.routerId = ctx.requireString(c, "routerId")
|
|
entity.binding = ctx.requireString(c, "binding")
|
|
entity.address = ctx.requireString(c, "address")
|
|
entity.precedence = ctx.requireString(c, "precedence")
|
|
entity.cost = ctx.requireInt(c, "cost")
|
|
}
|
|
|
|
func newTestIdentity(isAdmin bool, roleAttributes ...string) *identity {
|
|
return &identity{
|
|
name: eid.New(),
|
|
identityType: string(rest_model.IdentityTypeDefault),
|
|
isAdmin: isAdmin,
|
|
roleAttributes: roleAttributes,
|
|
}
|
|
}
|
|
|
|
type identity struct {
|
|
Id string
|
|
name string
|
|
identityType string
|
|
isAdmin bool
|
|
enrollment map[string]interface{}
|
|
roleAttributes []string
|
|
tags map[string]interface{}
|
|
defaultHostingPrecedence string
|
|
defaultHostingCost int
|
|
serviceHostingPrecedences map[string]interface{}
|
|
serviceHostingCosts map[string]uint16
|
|
config *ziti.Config
|
|
authPolicyId string
|
|
}
|
|
|
|
func (entity *identity) getId() string {
|
|
return entity.Id
|
|
}
|
|
|
|
func (entity *identity) setId(id string) {
|
|
entity.Id = id
|
|
}
|
|
|
|
func (entity *identity) getEntityType() string {
|
|
return "identities"
|
|
}
|
|
|
|
func (entity *identity) toJson(isCreate bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.name, "name")
|
|
ctx.setJsonValue(entityData, entity.identityType, "type")
|
|
ctx.setJsonValue(entityData, entity.isAdmin, "isAdmin")
|
|
ctx.setJsonValue(entityData, entity.enrollment, "enrollment")
|
|
ctx.setJsonValue(entityData, entity.roleAttributes, "roleAttributes")
|
|
if entity.defaultHostingPrecedence != "" {
|
|
ctx.setJsonValue(entityData, entity.defaultHostingPrecedence, "defaultHostingPrecedence")
|
|
}
|
|
if entity.defaultHostingCost != 0 {
|
|
ctx.setJsonValue(entityData, entity.defaultHostingCost, "defaultHostingCost")
|
|
}
|
|
ctx.setJsonValue(entityData, entity.serviceHostingPrecedences, "serviceHostingPrecedences")
|
|
ctx.setJsonValue(entityData, entity.serviceHostingCosts, "serviceHostingCosts")
|
|
ctx.setJsonValue(entityData, entity.authPolicyId, "authPolicyId")
|
|
|
|
if isCreate {
|
|
if entity.enrollment == nil {
|
|
enrollments := map[string]interface{}{
|
|
"updb": entity.name,
|
|
}
|
|
ctx.setJsonValue(entityData, enrollments, "enrollment")
|
|
}
|
|
}
|
|
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *identity) getCompareServiceHostingsCosts() map[string]interface{} {
|
|
if entity.serviceHostingCosts == nil {
|
|
return nil
|
|
}
|
|
result := map[string]interface{}{}
|
|
for k, v := range entity.serviceHostingCosts {
|
|
result[k] = float64(v)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (entity *identity) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
if entity.serviceHostingCosts == nil {
|
|
entity.serviceHostingCosts = map[string]uint16{}
|
|
}
|
|
if entity.serviceHostingPrecedences == nil {
|
|
entity.serviceHostingPrecedences = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
if entity.defaultHostingPrecedence != "" {
|
|
ctx.pathEquals(c, entity.defaultHostingPrecedence, path("defaultHostingPrecedence"))
|
|
} else {
|
|
ctx.pathEquals(c, "default", path("defaultHostingPrecedence"))
|
|
}
|
|
|
|
ctx.pathEquals(c, entity.defaultHostingCost, path("defaultHostingCost"))
|
|
|
|
if entity.roleAttributes == nil {
|
|
entity.roleAttributes = []string{}
|
|
}
|
|
sort.Strings(entity.roleAttributes)
|
|
ctx.pathEqualsStringSlice(c, entity.roleAttributes, path("roleAttributes"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
ctx.pathEquals(c, entity.serviceHostingPrecedences, path("serviceHostingPrecedences"))
|
|
ctx.pathEquals(c, entity.getCompareServiceHostingsCosts(), path("serviceHostingCosts"))
|
|
}
|
|
|
|
func (entity *identity) fromJson(ctx *TestContext, c *gabs.Container) {
|
|
entity.Id = ctx.requireString(c, "id")
|
|
entity.identityType = ctx.requireString(c, "type", "id")
|
|
entity.isAdmin = ctx.requireBool(c, "isAdmin")
|
|
entity.name = ctx.requireString(c, "name")
|
|
entity.roleAttributes = ctx.requireStringSlice(c, "roleAttributes")
|
|
}
|
|
|
|
func newTestEdgeRouter(roleAttributes ...string) *edgeRouter {
|
|
return &edgeRouter{
|
|
name: eid.New(),
|
|
roleAttributes: roleAttributes,
|
|
}
|
|
}
|
|
|
|
type edgeRouter struct {
|
|
id string
|
|
name string
|
|
isTunnelerEnabled bool
|
|
roleAttributes []string
|
|
configs []string
|
|
ctrlChanListeners map[string][]string
|
|
tags map[string]interface{}
|
|
}
|
|
|
|
func (entity *edgeRouter) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *edgeRouter) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *edgeRouter) getEntityType() string {
|
|
return "edge-routers"
|
|
}
|
|
|
|
func (entity *edgeRouter) toJson(_ bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.name, "name")
|
|
ctx.setJsonValue(entityData, entity.roleAttributes, "roleAttributes")
|
|
ctx.setJsonValue(entityData, entity.isTunnelerEnabled, "isTunnelerEnabled")
|
|
if len(entity.configs) > 0 {
|
|
ctx.setJsonValue(entityData, entity.configs, "configs")
|
|
}
|
|
listeners := entity.ctrlChanListeners
|
|
if listeners == nil {
|
|
listeners = map[string][]string{}
|
|
}
|
|
ctx.setJsonValue(entityData, listeners, "ctrlChanListeners")
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *edgeRouter) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
ctx.pathEquals(c, entity.isTunnelerEnabled, path("isTunnelerEnabled"))
|
|
sort.Strings(entity.configs)
|
|
ctx.pathEqualsStringSlice(c, entity.configs, path("configs"))
|
|
sort.Strings(entity.roleAttributes)
|
|
ctx.pathEqualsStringSlice(c, entity.roleAttributes, path("roleAttributes"))
|
|
ctx.pathEqualsCtrlChanListeners(c, entity.ctrlChanListeners, path("ctrlChanListeners"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
}
|
|
|
|
func newEdgeRouterPolicy(semantic string, edgeRouterRoles, identityRoles []string) *edgeRouterPolicy {
|
|
return &edgeRouterPolicy{
|
|
name: eid.New(),
|
|
semantic: semantic,
|
|
edgeRouterRoles: edgeRouterRoles,
|
|
identityRoles: identityRoles,
|
|
}
|
|
}
|
|
|
|
type edgeRouterPolicy struct {
|
|
id string
|
|
name string
|
|
semantic string
|
|
edgeRouterRoles []string
|
|
identityRoles []string
|
|
tags map[string]interface{}
|
|
}
|
|
|
|
func (entity *edgeRouterPolicy) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *edgeRouterPolicy) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *edgeRouterPolicy) getEntityType() string {
|
|
return "edge-router-policies"
|
|
}
|
|
|
|
func (entity *edgeRouterPolicy) toJson(_ bool, ctx *TestContext, fields ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setValue(entityData, entity.name, fields, "name")
|
|
ctx.setValue(entityData, entity.semantic, fields, "semantic")
|
|
ctx.setValue(entityData, entity.edgeRouterRoles, fields, "edgeRouterRoles")
|
|
ctx.setValue(entityData, entity.identityRoles, fields, "identityRoles")
|
|
ctx.setValue(entityData, entity.tags, fields, "tags")
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *edgeRouterPolicy) fromJson(ctx *TestContext, c *gabs.Container) {
|
|
entity.id = ctx.requireString(c, "id")
|
|
entity.name = ctx.requireString(c, "name")
|
|
entity.semantic = ctx.requireString(c, "semantic")
|
|
entity.edgeRouterRoles = ctx.requireStringSlice(c, "edgeRouterRoles")
|
|
entity.identityRoles = ctx.requireStringSlice(c, "identityRoles")
|
|
}
|
|
|
|
func (entity *edgeRouterPolicy) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
ctx.pathEquals(c, entity.semantic, path("semantic"))
|
|
sort.Strings(entity.edgeRouterRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.edgeRouterRoles, path("edgeRouterRoles"))
|
|
sort.Strings(entity.identityRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.identityRoles, path("identityRoles"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
}
|
|
|
|
func newServiceEdgeRouterPolicy(semantic string, edgeRouterRoles, serviceRoles []string) *serviceEdgeRouterPolicy {
|
|
return &serviceEdgeRouterPolicy{
|
|
name: eid.New(),
|
|
semantic: semantic,
|
|
edgeRouterRoles: edgeRouterRoles,
|
|
serviceRoles: serviceRoles,
|
|
}
|
|
}
|
|
|
|
type serviceEdgeRouterPolicy struct {
|
|
id string
|
|
name string
|
|
semantic string
|
|
edgeRouterRoles []string
|
|
serviceRoles []string
|
|
tags map[string]interface{}
|
|
}
|
|
|
|
func (entity *serviceEdgeRouterPolicy) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *serviceEdgeRouterPolicy) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *serviceEdgeRouterPolicy) getEntityType() string {
|
|
return "service-edge-router-policies"
|
|
}
|
|
|
|
func (entity *serviceEdgeRouterPolicy) toJson(_ bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.name, "name")
|
|
ctx.setJsonValue(entityData, entity.semantic, "semantic")
|
|
ctx.setJsonValue(entityData, entity.edgeRouterRoles, "edgeRouterRoles")
|
|
ctx.setJsonValue(entityData, entity.serviceRoles, "serviceRoles")
|
|
|
|
if len(entity.tags) > 0 {
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
}
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *serviceEdgeRouterPolicy) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
ctx.pathEquals(c, entity.semantic, path("semantic"))
|
|
sort.Strings(entity.edgeRouterRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.edgeRouterRoles, path("edgeRouterRoles"))
|
|
sort.Strings(entity.serviceRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.serviceRoles, path("serviceRoles"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
}
|
|
|
|
func newServicePolicy(policyType string, semantic string, serviceRoles, identityRoles, postureCheckRoles []string) *servicePolicy {
|
|
return &servicePolicy{
|
|
name: eid.New(),
|
|
policyType: policyType,
|
|
semantic: semantic,
|
|
serviceRoles: serviceRoles,
|
|
identityRoles: identityRoles,
|
|
postureCheckRoles: postureCheckRoles,
|
|
}
|
|
}
|
|
|
|
type servicePolicy struct {
|
|
id string
|
|
name string
|
|
policyType string
|
|
semantic string
|
|
identityRoles []string
|
|
serviceRoles []string
|
|
tags map[string]interface{}
|
|
postureCheckRoles []string
|
|
}
|
|
|
|
func (entity *servicePolicy) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *servicePolicy) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *servicePolicy) getEntityType() string {
|
|
return "service-policies"
|
|
}
|
|
|
|
func (entity *servicePolicy) toJson(_ bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.name, "name")
|
|
ctx.setJsonValue(entityData, entity.policyType, "type")
|
|
ctx.setJsonValue(entityData, entity.semantic, "semantic")
|
|
ctx.setJsonValue(entityData, entity.identityRoles, "identityRoles")
|
|
ctx.setJsonValue(entityData, entity.serviceRoles, "serviceRoles")
|
|
ctx.setJsonValue(entityData, entity.postureCheckRoles, "postureCheckRoles")
|
|
|
|
if len(entity.tags) > 0 {
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
}
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *servicePolicy) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
ctx.pathEquals(c, entity.policyType, path("type"))
|
|
ctx.pathEquals(c, entity.semantic, path("semantic"))
|
|
sort.Strings(entity.identityRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.identityRoles, path("identityRoles"))
|
|
sort.Strings(entity.serviceRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.serviceRoles, path("serviceRoles"))
|
|
sort.Strings(entity.postureCheckRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.postureCheckRoles, path("postureCheckRoles"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
}
|
|
|
|
type Config struct {
|
|
Id string
|
|
ConfigTypeId string
|
|
Name string
|
|
Data map[string]interface{}
|
|
Tags map[string]interface{}
|
|
sendType bool
|
|
}
|
|
|
|
func (entity *Config) getId() string {
|
|
return entity.Id
|
|
}
|
|
|
|
func (entity *Config) setId(id string) {
|
|
entity.Id = id
|
|
}
|
|
|
|
func (entity *Config) getEntityType() string {
|
|
return "configs"
|
|
}
|
|
|
|
func (entity *Config) toJson(isCreate bool, ctx *TestContext, fields ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setValue(entityData, entity.Name, fields, "name")
|
|
if isCreate || entity.sendType {
|
|
ctx.setValue(entityData, entity.ConfigTypeId, fields, "configTypeId")
|
|
}
|
|
ctx.setValue(entityData, entity.Data, fields, "data")
|
|
ctx.setValue(entityData, entity.Tags, fields, "tags")
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *Config) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.Tags == nil {
|
|
entity.Tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.Name, path("name"))
|
|
ctx.pathEquals(c, entity.ConfigTypeId, path("configTypeId"))
|
|
ctx.pathEquals(c, entity.Data, path("data"))
|
|
ctx.pathEquals(c, entity.Tags, path("tags"))
|
|
}
|
|
|
|
type configType struct {
|
|
Id string
|
|
Name string
|
|
Schema map[string]interface{}
|
|
Target *string
|
|
Tags map[string]interface{}
|
|
}
|
|
|
|
func (entity *configType) getId() string {
|
|
return entity.Id
|
|
}
|
|
|
|
func (entity *configType) setId(id string) {
|
|
entity.Id = id
|
|
}
|
|
|
|
func (entity *configType) getEntityType() string {
|
|
return "config-types"
|
|
}
|
|
|
|
func (entity *configType) toJson(_ bool, ctx *TestContext, fields ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setValue(entityData, entity.Name, fields, "name")
|
|
ctx.setValue(entityData, entity.Schema, fields, "schema")
|
|
ctx.setValue(entityData, derefOrNil(entity.Target), fields, "target")
|
|
ctx.setValue(entityData, entity.Tags, fields, "tags")
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *configType) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.Tags == nil {
|
|
entity.Tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.Name, path("name"))
|
|
ctx.pathEquals(c, entity.Schema, path("schema"))
|
|
ctx.pathEquals(c, derefOrNil(entity.Target), path("target"))
|
|
ctx.pathEquals(c, entity.Tags, path("tags"))
|
|
}
|
|
|
|
type configValidatingService struct {
|
|
*service
|
|
configs map[string]*Config
|
|
}
|
|
|
|
func (entity *configValidatingService) validate(ctx *TestContext, c *gabs.Container) {
|
|
configs := c.Path("config")
|
|
if len(entity.configs) == 0 && configs == nil {
|
|
return
|
|
}
|
|
|
|
children, err := configs.Children()
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(len(entity.configs), len(children))
|
|
for configType, cfg := range entity.configs {
|
|
ctx.pathEquals(configs, cfg.Data, s(configType))
|
|
}
|
|
}
|
|
|
|
func newTestTransitRouter() *transitRouter {
|
|
return &transitRouter{
|
|
name: eid.New(),
|
|
}
|
|
}
|
|
|
|
type transitRouter struct {
|
|
id string
|
|
name string
|
|
configs []string
|
|
ctrlChanListeners map[string][]string
|
|
tags map[string]interface{}
|
|
}
|
|
|
|
func (entity *transitRouter) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *transitRouter) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *transitRouter) getEntityType() string {
|
|
return "transit-routers"
|
|
}
|
|
|
|
func (entity *transitRouter) toJson(_ bool, ctx *TestContext, _ ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setJsonValue(entityData, entity.name, "name")
|
|
if len(entity.configs) > 0 {
|
|
ctx.setJsonValue(entityData, entity.configs, "configs")
|
|
}
|
|
listeners := entity.ctrlChanListeners
|
|
if listeners == nil {
|
|
listeners = map[string][]string{}
|
|
}
|
|
ctx.setJsonValue(entityData, listeners, "ctrlChanListeners")
|
|
ctx.setJsonValue(entityData, entity.tags, "tags")
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *transitRouter) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
sort.Strings(entity.configs)
|
|
ctx.pathEqualsStringSlice(c, entity.configs, path("configs"))
|
|
ctx.pathEqualsCtrlChanListeners(c, entity.ctrlChanListeners, path("ctrlChanListeners"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
}
|
|
|
|
type ca struct {
|
|
id string
|
|
name string
|
|
isAutoCaEnrollmentEnabled bool
|
|
isAuthEnabled bool
|
|
isOttCaEnrollmentEnabled bool
|
|
certPem string
|
|
identityRoles []string
|
|
identityNameFormat string
|
|
tags map[string]interface{}
|
|
externalIdClaim *externalIdClaim
|
|
|
|
privateKey crypto.Signer //utility property, not used in API calls
|
|
publicCert *x509.Certificate //utility property, not used in API calls
|
|
}
|
|
|
|
type externalIdClaim struct {
|
|
location string
|
|
matcher string
|
|
matcherCriteria string
|
|
parser string
|
|
parserCriteria string
|
|
index int64
|
|
}
|
|
|
|
func newTestCaCert() (*x509.Certificate, *ecdsa.PrivateKey, *bytes.Buffer) {
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
caCert := &x509.Certificate{
|
|
SerialNumber: big.NewInt(2019),
|
|
Subject: pkix.Name{
|
|
Organization: []string{"Ziti Dev"},
|
|
Country: []string{"US"},
|
|
Province: []string{"Anywhere"},
|
|
Locality: []string{"Anytime"},
|
|
CommonName: "randomTestCa-" + uuid.NewString(),
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().AddDate(0, 0, 1),
|
|
IsCA: true,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
|
BasicConstraintsValid: true,
|
|
}
|
|
|
|
caBytes, err := x509.CreateCertificate(rand.Reader, caCert, caCert, &key.PublicKey, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
caCert, err = x509.ParseCertificate(caBytes)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
caPEM := new(bytes.Buffer)
|
|
_ = pem.Encode(caPEM, &pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: caBytes,
|
|
})
|
|
|
|
return caCert, key, caPEM
|
|
}
|
|
|
|
func newTestCa(identityRoles ...string) *ca {
|
|
caCert, key, caPEM := newTestCaCert()
|
|
|
|
if identityRoles == nil {
|
|
identityRoles = []string{}
|
|
}
|
|
|
|
return &ca{
|
|
name: eid.New(),
|
|
isAutoCaEnrollmentEnabled: true,
|
|
isAuthEnabled: true,
|
|
isOttCaEnrollmentEnabled: true,
|
|
certPem: caPEM.String(),
|
|
identityRoles: identityRoles,
|
|
identityNameFormat: "[caName]-[commonName]-[requestedName]",
|
|
tags: map[string]interface{}{},
|
|
privateKey: key,
|
|
publicCert: caCert,
|
|
}
|
|
}
|
|
|
|
func (entity *ca) getId() string {
|
|
return entity.id
|
|
}
|
|
|
|
func (entity *ca) setId(id string) {
|
|
entity.id = id
|
|
}
|
|
|
|
func (entity *ca) getEntityType() string {
|
|
return "cas"
|
|
}
|
|
|
|
func (entity *ca) toJson(create bool, ctx *TestContext, fields ...string) string {
|
|
entityData := gabs.New()
|
|
ctx.setValue(entityData, entity.name, fields, "name")
|
|
ctx.setValue(entityData, entity.isOttCaEnrollmentEnabled, fields, "isOttCaEnrollmentEnabled")
|
|
ctx.setValue(entityData, entity.isAutoCaEnrollmentEnabled, fields, "isAutoCaEnrollmentEnabled")
|
|
ctx.setValue(entityData, entity.isAuthEnabled, fields, "isAuthEnabled")
|
|
ctx.setValue(entityData, entity.identityRoles, fields, "identityRoles")
|
|
ctx.setValue(entityData, entity.tags, fields, "tags")
|
|
ctx.setValue(entityData, entity.identityNameFormat, fields, "identityNameFormat")
|
|
|
|
if entity.externalIdClaim != nil {
|
|
ctx.setValueWithPath(entityData, entity.externalIdClaim.location, fields, "externalIdClaim", "externalIdClaim", "location")
|
|
ctx.setValueWithPath(entityData, entity.externalIdClaim.index, fields, "externalIdClaim", "externalIdClaim", "index")
|
|
ctx.setValueWithPath(entityData, entity.externalIdClaim.matcher, fields, "externalIdClaim", "externalIdClaim", "matcher")
|
|
ctx.setValueWithPath(entityData, entity.externalIdClaim.matcherCriteria, fields, "externalIdClaim", "externalIdClaim", "matcherCriteria")
|
|
ctx.setValueWithPath(entityData, entity.externalIdClaim.parser, fields, "externalIdClaim", "externalIdClaim", "parser")
|
|
ctx.setValueWithPath(entityData, entity.externalIdClaim.parserCriteria, fields, "externalIdClaim", "externalIdClaim", "parserCriteria")
|
|
}
|
|
|
|
if create {
|
|
ctx.setValue(entityData, entity.certPem, fields, "certPem")
|
|
}
|
|
|
|
return entityData.String()
|
|
}
|
|
|
|
func (entity *ca) validate(ctx *TestContext, c *gabs.Container) {
|
|
if entity.tags == nil {
|
|
entity.tags = map[string]interface{}{}
|
|
}
|
|
ctx.pathEquals(c, entity.name, path("name"))
|
|
sort.Strings(entity.identityRoles)
|
|
ctx.pathEqualsStringSlice(c, entity.identityRoles, path("identityRoles"))
|
|
ctx.pathEquals(c, entity.certPem, path("certPem"))
|
|
ctx.pathEquals(c, entity.isAuthEnabled, path("isAuthEnabled"))
|
|
ctx.pathEquals(c, entity.isAutoCaEnrollmentEnabled, path("isAutoCaEnrollmentEnabled"))
|
|
ctx.pathEquals(c, entity.isOttCaEnrollmentEnabled, path("isOttCaEnrollmentEnabled"))
|
|
ctx.pathEquals(c, entity.identityNameFormat, path("identityNameFormat"))
|
|
ctx.pathEquals(c, entity.tags, path("tags"))
|
|
}
|
|
|
|
func (entity *ca) CreateSignedCert(name string) *certAuthenticator {
|
|
clientKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
csrTemplate := &x509.CertificateRequest{
|
|
Subject: pkix.Name{
|
|
CommonName: name,
|
|
Organization: []string{"Ziti Dev"},
|
|
Country: []string{"US"},
|
|
Province: []string{"Anywhere"},
|
|
Locality: []string{"Anytime"},
|
|
},
|
|
}
|
|
|
|
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, clientKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
csr, err := x509.ParseCertificateRequest(csrBytes)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err = csr.CheckSignature(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
certTemplate := x509.Certificate{
|
|
Signature: csr.Signature,
|
|
|
|
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
|
|
PublicKey: csr.PublicKey,
|
|
|
|
SerialNumber: big.NewInt(2020),
|
|
Issuer: entity.publicCert.Subject,
|
|
Subject: csr.Subject,
|
|
NotBefore: time.Now().AddDate(0, 0, -1),
|
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageDataEncipherment,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
IsCA: false,
|
|
}
|
|
|
|
clientBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, entity.publicCert, csr.PublicKey, entity.privateKey)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
clientCert, err := x509.ParseCertificate(clientBytes)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
clientPEM := new(bytes.Buffer)
|
|
_ = pem.Encode(clientPEM, &pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: clientBytes,
|
|
})
|
|
|
|
return &certAuthenticator{
|
|
certs: []*x509.Certificate{clientCert},
|
|
key: clientKey,
|
|
certPem: clientPEM.String(),
|
|
}
|
|
}
|
|
|
|
// copyRestModelFields copies fields with matching names and types from source to dest using reflection.
|
|
// This is useful for converting between rest model types (e.g., IdentityDetail to IdentityUpdate).
|
|
// Both source and dest must be pointers to structs.
|
|
func copyRestModelFields(source, dest interface{}, ctx *TestContext) {
|
|
srcVal := reflect.ValueOf(source)
|
|
destVal := reflect.ValueOf(dest)
|
|
|
|
// Dereference pointers
|
|
if srcVal.Kind() == reflect.Pointer {
|
|
srcVal = srcVal.Elem()
|
|
}
|
|
|
|
if destVal.Kind() == reflect.Pointer {
|
|
destVal = destVal.Elem()
|
|
}
|
|
|
|
// Ensure both are structs
|
|
if srcVal.Kind() != reflect.Struct || destVal.Kind() != reflect.Struct {
|
|
ctx.Fail("both source and dest must be structs, got %v and %v", srcVal.Kind(), destVal.Kind())
|
|
return
|
|
}
|
|
|
|
destType := destVal.Type()
|
|
|
|
// Iterate through destination fields
|
|
for i := 0; i < destVal.NumField(); i++ {
|
|
destField := destVal.Field(i)
|
|
destFieldType := destType.Field(i)
|
|
|
|
// Skip unexported fields
|
|
if !destField.CanSet() {
|
|
continue
|
|
}
|
|
|
|
// Find matching field in source by name
|
|
srcField := srcVal.FieldByName(destFieldType.Name)
|
|
if !srcField.IsValid() {
|
|
continue
|
|
}
|
|
|
|
// Check if types match
|
|
if srcField.Type() == destField.Type() {
|
|
// Direct copy for matching types
|
|
destField.Set(srcField)
|
|
} else if srcField.Type().ConvertibleTo(destField.Type()) {
|
|
// Convert if possible (e.g., concrete type to interface)
|
|
destField.Set(srcField.Convert(destField.Type()))
|
|
}
|
|
}
|
|
}
|