Merge pull request #123 from netfoundry/tx.router.extended.base

Tx.router.extended.base
This commit is contained in:
Andrew
2020-04-01 15:08:21 -04:00
committed by GitHub
48 changed files with 1716 additions and 277 deletions
@@ -0,0 +1,13 @@
{
"$id": "http://ziti-edge.netfoundry.io/schemas/fabric-routers.patch.json",
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"$ref": "definitions.all.json#/definitions/name-null"
},
"tags": {
"$ref": "definitions.all.json#/definitions/tags-null"
}
}
}
@@ -0,0 +1,16 @@
{
"$id": "http://ziti-edge.netfoundry.io/schemas/fabric-routers.post.json",
"type": "object",
"additionalProperties": true,
"required": [
"name"
],
"properties": {
"name": {
"$ref": "definitions.all.json#/definitions/name"
},
"tags": {
"$ref": "definitions.all.json#/definitions/tags-null"
}
}
}
@@ -0,0 +1,17 @@
{
"$id": "http://ziti-edge.netfoundry.io/schemas/fabric-routers.put.json",
"type": "object",
"additionalProperties": true,
"required": [
"name",
"tags"
],
"properties": {
"name": {
"$ref": "definitions.all.json#/definitions/name"
},
"tags": {
"$ref": "definitions.all.json#/definitions/tags"
}
}
}
+3
View File
@@ -52,6 +52,9 @@ func init() {
_ = packr.PackJSONBytes("../embedded", "api-schema/entities/terminator.patch.json", "\"H4sIAAAAAAAA/6yRsW7DMAxE93yFoGZspN1f0T3IoJi0zUCRVIoa0sL/XlBxm3TPYBh3PLwjqO+dMXZPYAdjF5EyeP9FQgeEGV1CmXJLwDdH2ddxwWuoXpCvlIJkdiXIuLhLzcm+K0huBZWUzxcc5e4FABLKKcQPzgVZCKsdjHDDPmf8bMSoGxx3xpy6WZ6juqMGcxPkP/2oO27aGFuFKc29eHNSi9Fu8tT/631qz5RAwy8DBgDGWl8HlDD/owFOoUVRa/0F2D3jpDcHnCj1S1cXYuyv8uafXK+4w6Nt3em3/gQAAP//ec1C3AICAAA=\"")
_ = packr.PackJSONBytes("../embedded", "api-schema/entities/terminator.post.json", "\"H4sIAAAAAAAA/4yRMW7rMAyGd5+C0MuYWLtP8fYgg2LRDgNFckmqQBr47oXUuLHRJYMHfv75kZIeDYDZkTcdmIvq1Fn7RUoH9CO2EXVIOXq+t5Ss9Be8ObGKfKPoNHE7JdH2KimaffHofcIiSucr9vrDnPeklKIL/zlNyEoopgPljPU/40cmxrLAsQEAMIL8ST3W9hJIWZGXynnPKGIagFPtn9bWx9awgNVqokxxNBXP2wlvps8UfYF/48dnDb+N+xeJOQTzLE8b4XKkN+erGzdZj4PLQQual3lmxzgUgceBYr1/aV0I9a3+2RW1RXd4LTc35Zu/AwAA//9siIA/FwIAAA==\"")
_ = packr.PackJSONBytes("../embedded", "api-schema/entities/terminator.put.json", "\"H4sIAAAAAAAA/4yQsW7DMAxEd38FoWZMrD1f0T3IoFi0w0CRXJIqkAb+90Kq3djo0kHDnXiPBz4bALMjb45grqrj0dovUjqgH7CNqH3K0fOjpWSlu+LdiVXkO0WnidsxibY3SdHsC0cfIxZQutyw0x/PeU9KKbrwzmlEVkIxR1DOWP8ZPzIxlgKnBgDACPIndVjjZSBlRV6U855RxDQA55of19TnJrLoVTNRpjiYak8z8kLRF/Pv+GnW8Bvcv5yYQzCzPG+AS8d/7lc3bGY99i4HLda07DM7xr4APPYU60GldSHU47/ZlWsL7vAqNzXlTd8BAAD//xFy8DDoAQAA\"")
_ = packr.PackJSONBytes("../embedded", "api-schema/entities/transitRouter.patch.json", "\"H4sIAAAAAAAA/5zOMW7EIBAF0N6nQCTlGnqfIleYhWE9KxbQMBSbyHePBkuR6xQUfPQf/2cxxn5StJuxu0jbvP8moRXjA11BSXWUyG9H1few4wu6T3BnCivXIcjdNZCwu2evxd4Uk3dD1er9iUHODGIkoVogf3FtyELY7WaEB873dk11kjG2wAv/bjqSMakbMVGZWneQ8/z5w19Sr821jJzt7B63ExR49P+B2ryCi55j+Q0AAP//uFLZijwBAAA=\"")
_ = packr.PackJSONBytes("../embedded", "api-schema/entities/transitRouter.post.json", "\"H4sIAAAAAAAA/5yPsXIDIQxEe75CQ1L6oL+vSJ9JgQ+dLQ9GRBKF4/G/ZzjHM1enoGCXfbvcHYB/p+xn8GezNsf4Q0YT5hOGirZyr1lugTjqcsZr0rimo9AyCXdD0dBYLVyUqz8Mlt0aDhgfL7jYU0s5kxHXVD6EG4oRqp/BpOPmC353EhwjPh0AgK/pit4BfG1+26fuuxev2/iD4Dp6M65UtzYNqZRt2VvcqfHFBngcnixLJ/0faySn2kv5A7pxHu43AAD//+YEIOVWAQAA\"")
_ = packr.PackJSONBytes("../embedded", "api-schema/entities/transitRouter.put.json", "\"H4sIAAAAAAAA/5yPsW7EIAyGd57Coh17sOcpulcduGDuHCVAjRnSKO9eQRWJ+QYGjP/v/zgUgH4nryfQT5E8WftLQjf0DzQRJaQaPe+Gki3zEzdXbHB3pvnGqQpyMbmKWUqK+qOhZM/YWOm+4Cz/M+c9CaXo1k9OGVkIi55AuGJ/Z/ypxNgcvhQAgI5uw55tRPcoWgF89908Eo5h+7q17zCG5uAxUOzNxbh17ZZvdpjanuyxc2x7iXV5ApyqnVP9BQAA//+mADgbXAEAAA==\"")
_ = packr.PackJSONBytes("../embedded", "db/postgres/migrations/20180828083820_init-schema.down.sql", "\"H4sIAAAAAAAA/1SNMcvCMBiE9/dX3BDI9y3+geJQk7c2YFNpInYrQaNmaAuayV8vtCB4yw13D49uIQTteG8sEWAqcG+cd3+EJY4PrDxel0ccwzCFMaLq2gZpus3PMeQ0T8M6btbKAeeaO/5BtpDvlNMQr/coCfgnwNdsFwn3rE6eIXXXHuFUzU2J7x2qdKrULAsC2GqYqiBiq0mI4hMAAP//7wIpBL4AAAA=\"")
_ = packr.PackJSONBytes("../embedded", "db/postgres/migrations/20180828083820_init-schema.up.sql", "\"H4sIAAAAAAAA/2SNsUrGMBzE9/9T3BCILr5AcIjJ1Qb8UkkiflsJGjVD+4F28umFFgTxlhvufvz8BKXkjvchigBhAM8hl3wl2JP5QFfw9fLRljqvdWkY0nRCX98un0vd+mWdj/HmqK3ieWTiH+QW+rtvfW6v700LcC1AGRl3Cc90T4XQPk2PyG7kyeL3Dmezs57aCMDoEQYjwuhFKSPiEm3hP8r8BAAA//+lS2Hg2AAAAA==\"")
_ = packr.PackJSONBytes("../embedded", "db/postgres/migrations/20180905100347_initial-tables.down.sql", "\"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=\"")
+1
View File
@@ -149,6 +149,7 @@ type Schemes struct {
EnrollUpdb *BasicEntitySchema
EdgeRouter *BasicEntitySchema
EdgeRouterPolicy *BasicEntitySchema
TransitRouter *BasicEntitySchema
Identity *IdentityEntitySchema
Service *BasicEntitySchema
ServiceEdgeRouterPolicy *BasicEntitySchema
File diff suppressed because one or more lines are too long
@@ -150,17 +150,40 @@ func MapEdgeRouterToApiList(ae *env.AppEnv, i *model.EdgeRouter) (*EdgeRouterApi
BaseApi: env.FromBaseModelEntity(i),
Name: &i.Name,
RoleAttributes: i.RoleAttributes,
EnrollmentToken: i.EnrollmentToken,
EnrollmentCreatedAt: i.EnrollmentCreatedAt,
EnrollmentExpiresAt: i.EnrollmentExpiresAt,
EnrollmentToken: nil,
EnrollmentCreatedAt: nil,
EnrollmentExpiresAt: nil,
EnrollmentJwt: nil,
IsOnline: &isOnline,
IsVerified: &i.IsVerified,
Fingerprint: i.Fingerprint,
EnrollmentJwt: i.EnrollmentJwt,
Hostname: &hostname,
SupportedProtocols: protocols,
}
if !i.IsVerified {
var enrollments []*model.Enrollment
err := ae.GetHandlers().EdgeRouter.CollectEnrollments(i.Id, func(entity *model.Enrollment) error {
enrollments = append(enrollments, entity)
return nil
})
if err != nil {
return nil, err
}
if len(enrollments) != 1 {
return nil, fmt.Errorf("expected enrollment not found for unverified edge router %s", i.Id)
}
enrollment := enrollments[0]
ret.EnrollmentExpiresAt = enrollment.ExpiresAt
ret.EnrollmentCreatedAt = &enrollment.CreatedAt
ret.EnrollmentJwt = &enrollment.Jwt
ret.EnrollmentToken = &enrollment.Token
}
ret.PopulateLinks()
return ret, nil
@@ -31,11 +31,13 @@ const EntityNameEnrollment = "enrollments"
type EnrollmentApiList struct {
*env.BaseApi
Token *string `json:"token"`
Method *string `json:"method"`
ExpiresAt *time.Time `json:"expiresAt"`
Identity *EntityApiRef `json:"identity"`
Details interface{} `json:"details"`
Token *string `json:"token"`
Method *string `json:"method"`
ExpiresAt *time.Time `json:"expiresAt"`
Identity *EntityApiRef `json:"identity"`
Details interface{} `json:"details"`
EdgeRouter *EntityApiRef `json:"edgeRouter"`
TransitRouter *EntityApiRef `json:"transitRouter"`
}
func (e *EnrollmentApiList) GetSelfLink() *response.Link {
@@ -101,19 +103,35 @@ func MapEnrollmentToApiEntity(appEnv *env.AppEnv, context *response.RequestConte
}
func MapToEnrollmentApiList(ae *env.AppEnv, enrollment *model.Enrollment) (*EnrollmentApiList, error) {
identity, err := ae.Handlers.Identity.Read(enrollment.IdentityId)
if err != nil {
return nil, err
}
ret := &EnrollmentApiList{
BaseApi: env.FromBaseModelEntity(enrollment),
Token: &enrollment.Token,
Method: &enrollment.Method,
ExpiresAt: enrollment.ExpiresAt,
Identity: NewIdentityEntityRef(identity),
}
if enrollment.IdentityId != nil {
identity, err := ae.Handlers.Identity.Read(*enrollment.IdentityId)
if err != nil {
return nil, err
}
ret.Identity = NewIdentityEntityRef(identity)
}
if enrollment.EdgeRouterId != nil {
edgeRouter, err := ae.Handlers.EdgeRouter.Read(*enrollment.EdgeRouterId)
if err != nil {
return nil, err
}
ret.EdgeRouter = NewEdgeRouterEntityRef(edgeRouter)
}
if enrollment.TransitRouterId != nil {
transitRouter, err := ae.Handlers.TransitRouter.Read(*enrollment.TransitRouterId)
if err != nil {
return nil, err
}
ret.TransitRouter = NewTransitRouterEntityRef(transitRouter)
}
ret.PopulateLinks()
@@ -0,0 +1,130 @@
package routes
import (
"fmt"
"github.com/michaelquigley/pfxlog"
"github.com/netfoundry/ziti-edge/controller/env"
"github.com/netfoundry/ziti-edge/controller/model"
"github.com/netfoundry/ziti-edge/controller/response"
"github.com/netfoundry/ziti-fabric/controller/models"
"time"
)
const EntityNameTransitRouter = "transit-routers"
func NewTransitRouterEntityRef(entity *model.TransitRouter) *EntityApiRef {
links := &response.Links{
"self": NewTransitRouterLink(entity.Id),
}
return &EntityApiRef{
Entity: EntityNameEdgeRouter,
Id: entity.Id,
Name: &entity.Name,
Links: links,
}
}
func NewTransitRouterLink(id string) *response.Link {
return response.NewLink(fmt.Sprintf("./%s/%s", EntityNameTransitRouter, id))
}
type TransitRouterApi struct {
Name string `json:"name"`
Tags map[string]interface{} `json:"tags"`
}
func (i TransitRouterApi) ToModel(id string) *model.TransitRouter {
ret := &model.TransitRouter{
BaseEntity: models.BaseEntity{
Tags: i.Tags,
Id: id,
},
Name: i.Name,
}
return ret
}
type TransitRouterApiList struct {
*env.BaseApi
Name string `json:"name"`
Fingerprint string `json:"fingerprint"`
IsVerified bool `json:"isVerified"`
IsOnline bool `json:"isOnline"`
EnrollmentToken *string `json:"enrollmentToken"`
EnrollmentJwt *string `json:"enrollmentJwt"`
EnrollmentCreatedAt *time.Time `json:"enrollmentCreatedAt"`
EnrollmentExpiresAt *time.Time `json:"enrollmentExpiresAt"`
}
func (e TransitRouterApiList) GetSelfLink() *response.Link {
return e.BuildSelfLink(e.Id)
}
func (e TransitRouterApiList) PopulateLinks() {
if e.Links == nil {
e.Links = &response.Links{
EntityNameSelf: e.GetSelfLink(),
}
}
}
func (e TransitRouterApiList) ToEntityApiRef() *EntityApiRef {
e.PopulateLinks()
return &EntityApiRef{
Entity: EntityNameTransitRouter,
Name: nil,
Id: e.Id,
Links: e.Links,
}
}
func (e TransitRouterApiList) BuildSelfLink(id string) *response.Link {
return NewTransitRouterLink(id)
}
func MapTransitRouterToApiEntity(appEnv *env.AppEnv, context *response.RequestContext, entity models.Entity) (BaseApiEntity, error) {
txRouter, ok := entity.(*model.TransitRouter)
if !ok {
err := fmt.Errorf("entity is not a transit router \"%s\"", entity.GetId())
log := pfxlog.Logger()
log.Error(err)
return nil, err
}
ret := &TransitRouterApiList{
BaseApi: env.FromBaseModelEntity(entity),
Name: txRouter.Name,
Fingerprint: txRouter.Fingerprint,
IsVerified: txRouter.IsVerified,
IsOnline: appEnv.GetHandlers().Router.IsConnected(entity.GetId()),
}
if !txRouter.IsBase && !txRouter.IsVerified {
var enrollments []*model.Enrollment
err := appEnv.GetHandlers().TransitRouter.CollectEnrollments(txRouter.Id, func(entity *model.Enrollment) error {
enrollments = append(enrollments, entity)
return nil
})
if err != nil {
return nil, err
}
if len(enrollments) != 1 {
return nil, fmt.Errorf("expected enrollment not found for unverified transit router %s", txRouter.Id)
}
enrollment := enrollments[0]
ret.EnrollmentExpiresAt = enrollment.ExpiresAt
ret.EnrollmentCreatedAt = &enrollment.CreatedAt
ret.EnrollmentJwt = &enrollment.Jwt
ret.EnrollmentToken = &enrollment.Token
}
ret.PopulateLinks()
return ret, nil
}
@@ -0,0 +1,83 @@
/*
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 routes
import (
"github.com/netfoundry/ziti-edge/controller/env"
"github.com/netfoundry/ziti-edge/controller/internal/permissions"
"github.com/netfoundry/ziti-edge/controller/response"
)
func init() {
r := NewTransitRouterRouter()
env.AddRouter(r)
}
type TransitRouterRouter struct {
BasePath string
IdType response.IdType
}
func NewTransitRouterRouter() *TransitRouterRouter {
return &TransitRouterRouter{
BasePath: "/" + EntityNameTransitRouter,
IdType: response.IdTypeUuid,
}
}
func (ir *TransitRouterRouter) Register(ae *env.AppEnv) {
_ = registerCrudRouter(ae, ae.RootRouter, ir.BasePath, ir, &crudResolvers{
Create: permissions.IsAdmin(),
Read: permissions.IsAdmin(),
Update: permissions.IsAdmin(),
Delete: permissions.IsAdmin(),
Default: permissions.IsAdmin(),
})
}
func (ir *TransitRouterRouter) List(ae *env.AppEnv, rc *response.RequestContext) {
ListWithHandler(ae, rc, ae.Handlers.TransitRouter, MapTransitRouterToApiEntity)
}
func (ir *TransitRouterRouter) Detail(ae *env.AppEnv, rc *response.RequestContext) {
DetailWithHandler(ae, rc, ae.Handlers.TransitRouter, MapTransitRouterToApiEntity, ir.IdType)
}
func (ir *TransitRouterRouter) Create(ae *env.AppEnv, rc *response.RequestContext) {
transitRouterCreate := &TransitRouterApi{}
Create(rc, rc.RequestResponder, ae.Schemes.TransitRouter.Post, transitRouterCreate, (&TransitRouterApiList{}).BuildSelfLink, func() (string, error) {
return ae.Handlers.TransitRouter.Create(transitRouterCreate.ToModel(""))
})
}
func (ir *TransitRouterRouter) Delete(ae *env.AppEnv, rc *response.RequestContext) {
DeleteWithHandler(rc, ir.IdType, ae.Handlers.TransitRouter)
}
func (ir *TransitRouterRouter) Update(ae *env.AppEnv, rc *response.RequestContext) {
transitRouterUpdate := &TransitRouterApi{}
Update(rc, ae.Schemes.TransitRouter.Put, ir.IdType, transitRouterUpdate, func(id string) error {
return ae.Handlers.TransitRouter.Update(transitRouterUpdate.ToModel(id))
})
}
func (ir *TransitRouterRouter) Patch(ae *env.AppEnv, rc *response.RequestContext) {
transitRouterUpdate := &TransitRouterApi{}
Patch(rc, ae.Schemes.TransitRouter.Patch, ir.IdType, transitRouterUpdate, func(id string, fields JsonFields) error {
return ae.Handlers.TransitRouter.Patch(transitRouterUpdate.ToModel(id), fields.ConcatNestedNames().FilterMaps("tags"))
})
}
+81 -1
View File
@@ -18,6 +18,8 @@ package model
import (
"fmt"
"github.com/google/uuid"
"github.com/michaelquigley/pfxlog"
"github.com/netfoundry/ziti-edge/controller/persistence"
"github.com/netfoundry/ziti-fabric/controller/models"
"github.com/netfoundry/ziti-foundation/storage/boltz"
@@ -49,7 +51,14 @@ func (handler *EdgeRouterHandler) newModelEntity() boltEntitySink {
}
func (handler *EdgeRouterHandler) Create(modelEntity *EdgeRouter) (string, error) {
return handler.createEntity(modelEntity)
enrollment := &Enrollment{
BaseEntity: models.BaseEntity{},
Method: MethodEnrollEdgeRouterOtt,
}
id, _, err := handler.CreateWithEnrollment(modelEntity, enrollment)
return id, err
}
func (handler *EdgeRouterHandler) Read(id string) (*EdgeRouter, error) {
@@ -154,6 +163,77 @@ func (handler *EdgeRouterHandler) QueryRoleAttributes(queryString string) ([]str
return handler.queryRoleAttributes(index, queryString)
}
func (handler *EdgeRouterHandler) CreateWithEnrollment(edgeRouter *EdgeRouter, enrollment *Enrollment) (string, string, error) {
if edgeRouter.Id == "" {
edgeRouter.Id = uuid.New().String()
}
if enrollment.Id == "" {
enrollment.Id = uuid.New().String()
}
err := handler.GetDb().Update(func(tx *bbolt.Tx) error {
ctx := boltz.NewMutateContext(tx)
boltEdgeRouter, err := edgeRouter.toBoltEntityForCreate(tx, handler.impl)
if err != nil {
return err
}
if err := handler.GetStore().Create(ctx, boltEdgeRouter); err != nil {
pfxlog.Logger().WithError(err).Errorf("could not create %v in bolt storage", handler.GetStore().GetSingularEntityType())
return err
}
enrollment.EdgeRouterId = &edgeRouter.Id
err = enrollment.FillJwtInfo(handler.env, edgeRouter.Id)
if err != nil {
return err
}
_, err = handler.env.GetHandlers().Enrollment.createEntityInTx(ctx, enrollment)
if err != nil {
return err
}
return nil
})
if err != nil {
return "", "", err
}
return edgeRouter.Id, enrollment.Id, nil
}
func (handler *EdgeRouterHandler) CollectEnrollments(id string, collector func(entity *Enrollment) error) error {
return handler.GetDb().View(func(tx *bbolt.Tx) error {
return handler.collectEnrollmentsInTx(tx, id, collector)
})
}
func (handler *EdgeRouterHandler) collectEnrollmentsInTx(tx *bbolt.Tx, id string, collector func(entity *Enrollment) error) error {
_, err := handler.readInTx(tx, id)
if err != nil {
return err
}
associationIds := handler.GetStore().GetRelatedEntitiesIdList(tx, id, persistence.FieldEdgeRouterEnrollments)
for _, enrollmentId := range associationIds {
enrollment, err := handler.env.GetHandlers().Enrollment.readInTx(tx, enrollmentId)
if err != nil {
return err
}
err = collector(enrollment)
if err != nil {
return err
}
}
return nil
}
type EdgeRouterListResult struct {
handler *EdgeRouterHandler
EdgeRouters []*EdgeRouter
+5 -56
View File
@@ -17,17 +17,12 @@
package model
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/google/uuid"
"github.com/netfoundry/ziti-edge/controller/persistence"
"github.com/netfoundry/ziti-fabric/controller/models"
"github.com/netfoundry/ziti-foundation/storage/boltz"
"github.com/netfoundry/ziti-sdk-golang/ziti/config"
"github.com/pkg/errors"
"go.etcd.io/bbolt"
"reflect"
"time"
)
type EdgeRouter struct {
@@ -37,57 +32,19 @@ type EdgeRouter struct {
IsVerified bool
Fingerprint *string
CertPem *string
EnrollmentToken *string
Hostname *string
EnrollmentJwt *string
EnrollmentCreatedAt *time.Time
EnrollmentExpiresAt *time.Time
EdgeRouterProtocols map[string]string
}
func (entity *EdgeRouter) toBoltEntityForCreate(_ *bbolt.Tx, handler Handler) (boltz.Entity, error) {
et := uuid.New().String()
boltEntity := &persistence.EdgeRouter{
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
Name: entity.Name,
RoleAttributes: entity.RoleAttributes,
Fingerprint: nil,
IsVerified: false,
EnrollmentToken: &et,
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
Name: entity.Name,
RoleAttributes: entity.RoleAttributes,
Fingerprint: nil,
IsVerified: false,
}
env := handler.GetEnv()
now := time.Now()
exp := now.Add(env.GetConfig().Enrollment.EdgeRouter.DurationMinutes)
boltEntity.EnrollmentCreatedAt = &now
boltEntity.EnrollmentExpiresAt = &exp
enrollmentMethod := "erott"
claims := &config.EnrollmentClaims{
EnrollmentMethod: enrollmentMethod,
StandardClaims: jwt.StandardClaims{
ExpiresAt: exp.Unix(),
Id: *boltEntity.EnrollmentToken,
Issuer: fmt.Sprintf(`https://%s`, env.GetConfig().Api.Advertise),
Subject: boltEntity.Id,
},
}
mapClaims, err := claims.ToMapClaims()
if err != nil {
return nil, fmt.Errorf("could not convert edge router enrollment claims to interface map: %s", err)
}
jwtJson, err := env.GetEnrollmentJwtGenerator().Generate(boltEntity.Id, boltEntity.Id, mapClaims)
if err != nil {
return nil, err
}
boltEntity.EnrollmentJwt = &jwtJson
return boltEntity, nil
}
@@ -100,11 +57,7 @@ func (entity *EdgeRouter) toBoltEntityForUpdate(_ *bbolt.Tx, _ Handler) (boltz.E
IsVerified: entity.IsVerified,
Fingerprint: entity.Fingerprint,
CertPem: entity.CertPem,
EnrollmentToken: entity.EnrollmentToken,
Hostname: entity.Hostname,
EnrollmentJwt: entity.EnrollmentJwt,
EnrollmentCreatedAt: entity.EnrollmentCreatedAt,
EnrollmentExpiresAt: entity.EnrollmentExpiresAt,
EdgeRouterProtocols: entity.EdgeRouterProtocols,
}, nil
}
@@ -125,11 +78,7 @@ func (entity *EdgeRouter) fillFrom(_ Handler, _ *bbolt.Tx, boltEntity boltz.Enti
entity.IsVerified = boltEdgeRouter.IsVerified
entity.Fingerprint = boltEdgeRouter.Fingerprint
entity.CertPem = boltEdgeRouter.CertPem
entity.EnrollmentToken = boltEdgeRouter.EnrollmentToken
entity.Hostname = boltEdgeRouter.Hostname
entity.EnrollmentJwt = boltEdgeRouter.EnrollmentJwt
entity.EnrollmentCreatedAt = boltEdgeRouter.EnrollmentCreatedAt
entity.EnrollmentExpiresAt = boltEdgeRouter.EnrollmentExpiresAt
entity.EdgeRouterProtocols = boltEdgeRouter.EdgeRouterProtocols
return nil
+11 -15
View File
@@ -17,7 +17,6 @@
package model
import (
"fmt"
"github.com/netfoundry/ziti-edge/controller/apierror"
"github.com/netfoundry/ziti-edge/controller/persistence"
"github.com/netfoundry/ziti-foundation/storage/boltz"
@@ -57,22 +56,11 @@ func (handler *EnrollmentHandler) getEnrollmentMethod(ctx EnrollmentContext) (st
}
if enrollment == nil {
edgeRouter, err := handler.env.GetHandlers().EdgeRouter.ReadOneByQuery(fmt.Sprintf("enrollmentToken = \"%v\"", ctx.GetToken()))
if err != nil {
return "", err
}
if edgeRouter != nil {
method = MethodEnrollErOtt
} else {
return "", apierror.NewInvalidEnrollmentToken()
}
} else {
method = enrollment.Method
return "", apierror.NewInvalidEnrollmentToken()
}
method = enrollment.Method
return method, nil
}
@@ -142,3 +130,11 @@ func (handler *EnrollmentHandler) readInTx(tx *bbolt.Tx, id string) (*Enrollment
func (handler *EnrollmentHandler) Delete(id string) error {
return handler.deleteEntity(id)
}
func (handler *EnrollmentHandler) Read(id string) (*Enrollment, error) {
entity := &Enrollment{}
if err := handler.readEntity(id, entity); err != nil {
return nil, err
}
return entity, nil
}
+20 -16
View File
@@ -31,8 +31,7 @@ import (
const (
EdgeRouterEnrollmentCommonNameInvalidCode = "EDGE_ROUTER_ENROLL_COMMON_NAME_INVALID"
EdgeRouterEnrollmentCommonNameInvalidMessage = "The edge router CSR enrollment must have a common name that matches the edge router's id"
MethodEnrollErOtt = "erott"
MethodEnrollEdgeRouterOtt = "erott"
)
type EnrollModuleEr struct {
@@ -41,10 +40,10 @@ type EnrollModuleEr struct {
fingerprintGenerator cert.FingerprintGenerator
}
func NewEnrollModuleEr(env Env) *EnrollModuleEr {
func NewEnrollModuleEdgeRouterOtt(env Env) *EnrollModuleEr {
handler := &EnrollModuleEr{
env: env,
method: MethodEnrollErOtt,
method: MethodEnrollEdgeRouterOtt,
fingerprintGenerator: cert.NewFingerprintGenerator(),
}
@@ -56,18 +55,23 @@ func (module *EnrollModuleEr) CanHandle(method string) bool {
}
func (module *EnrollModuleEr) Process(context EnrollmentContext) (*EnrollmentResult, error) {
query := fmt.Sprintf(`isVerified = false and enrollmentToken = "%v"`, context.GetToken())
gw, err := module.env.GetHandlers().EdgeRouter.ReadOneByQuery(query)
enrollment, err := module.env.GetHandlers().Enrollment.ReadByToken(context.GetToken())
if err != nil {
return nil, err
}
if gw == nil {
if enrollment == nil || enrollment.EdgeRouterId == nil {
return nil, apierror.NewInvalidEnrollmentToken()
}
if time.Now().After(*gw.EnrollmentExpiresAt) {
edgeRouter, err := module.env.GetHandlers().EdgeRouter.Read(*enrollment.EdgeRouterId)
if edgeRouter == nil {
return nil, apierror.NewInvalidEnrollmentToken()
}
if time.Now().After(*enrollment.ExpiresAt) {
return nil, apierror.NewEnrollmentExpired()
}
@@ -136,7 +140,7 @@ func (module *EnrollModuleEr) Process(context EnrollmentContext) (*EnrollmentRes
URIs: cr.URIs,
}
if cr.Subject.CommonName != gw.Id {
if cr.Subject.CommonName != edgeRouter.Id {
return nil, &apierror.ApiError{
Code: EdgeRouterEnrollmentCommonNameInvalidCode,
@@ -162,16 +166,16 @@ func (module *EnrollModuleEr) Process(context EnrollmentContext) (*EnrollmentRes
cltFp := module.fingerprintGenerator.FromPem(cltPem)
gw.IsVerified = true
gw.EnrollmentCreatedAt = nil
gw.EnrollmentExpiresAt = nil
gw.EnrollmentJwt = nil
gw.EnrollmentToken = nil
gw.Fingerprint = &cltFp
if err := module.env.GetHandlers().EdgeRouter.Update(gw, false); err != nil {
edgeRouter.IsVerified = true
edgeRouter.Fingerprint = &cltFp
if err := module.env.GetHandlers().EdgeRouter.Update(edgeRouter, false); err != nil {
return nil, fmt.Errorf("could not update edge router: %s", err)
}
if err := module.env.GetHandlers().Enrollment.Delete(enrollment.Id); err != nil {
return nil, fmt.Errorf("could not delete enrollment: %s", err)
}
if err := module.createRouter(cr.Subject.CommonName, cltFp); err != nil {
return nil, err
}
+3 -3
View File
@@ -51,11 +51,11 @@ func (module *EnrollModuleOtt) Process(ctx EnrollmentContext) (*EnrollmentResult
return nil, err
}
if enrollment == nil {
if enrollment == nil || enrollment.IdentityId == nil {
return nil, apierror.NewInvalidEnrollmentToken()
}
identity, err := module.env.GetHandlers().Identity.Read(enrollment.IdentityId)
identity, err := module.env.GetHandlers().Identity.Read(*enrollment.IdentityId)
if err != nil {
return nil, err
@@ -86,7 +86,7 @@ func (module *EnrollModuleOtt) Process(ctx EnrollmentContext) (*EnrollmentResult
Id: uuid.New().String(),
},
Method: persistence.MethodAuthenticatorCert,
IdentityId: enrollment.IdentityId,
IdentityId: *enrollment.IdentityId,
SubType: &AuthenticatorCert{
Fingerprint: fp,
Pem: string(certPem),
+2 -2
View File
@@ -51,11 +51,11 @@ func (module *EnrollModuleOttCa) Process(ctx EnrollmentContext) (*EnrollmentResu
return nil, err
}
if enrollment == nil {
if enrollment == nil || enrollment.IdentityId == nil {
return nil, apierror.NewInvalidEnrollmentToken()
}
identity, err := module.env.GetHandlers().Identity.Read(enrollment.IdentityId)
identity, err := module.env.GetHandlers().Identity.Read(*enrollment.IdentityId)
if err != nil {
return nil, err
+182
View File
@@ -0,0 +1,182 @@
/*
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 model
import (
"encoding/json"
"fmt"
"github.com/netfoundry/ziti-edge/controller/apierror"
"github.com/netfoundry/ziti-edge/controller/validation"
"github.com/netfoundry/ziti-edge/internal/cert"
"github.com/xeipuuv/gojsonschema"
"time"
)
const (
MethodEnrollTransitRouterOtt = "trott"
)
type EnrollModuleRouterOtt struct {
env Env
method string
fingerprintGenerator cert.FingerprintGenerator
}
func NewEnrollModuleTransitRouterOtt(env Env) *EnrollModuleRouterOtt {
handler := &EnrollModuleRouterOtt{
env: env,
method: MethodEnrollTransitRouterOtt,
fingerprintGenerator: cert.NewFingerprintGenerator(),
}
return handler
}
func (module *EnrollModuleRouterOtt) CanHandle(method string) bool {
return method == module.method
}
func (module *EnrollModuleRouterOtt) Process(context EnrollmentContext) (*EnrollmentResult, error) {
enrollment, err := module.env.GetHandlers().Enrollment.ReadByToken(context.GetToken())
if err != nil {
return nil, err
}
if enrollment == nil || enrollment.TransitRouterId == nil {
return nil, apierror.NewInvalidEnrollmentToken()
}
txRouter, err := module.env.GetHandlers().TransitRouter.Read(*enrollment.TransitRouterId)
if txRouter == nil {
return nil, apierror.NewInvalidEnrollmentToken()
}
if time.Now().After(*enrollment.ExpiresAt) {
return nil, apierror.NewEnrollmentExpired()
}
enrollData := context.GetDataAsMap()
result, err := module.env.GetSchemas().GetEnrollErPost().Validate(gojsonschema.NewGoLoader(enrollData))
if err != nil {
return nil, err
}
if !result.Valid() {
var errs []*validation.SchemaValidationError
for _, re := range result.Errors() {
errs = append(errs, validation.NewValidationError(re))
}
apiError := apierror.NewCouldNotValidate()
if len(errs) > 0 {
apiError.Cause = errs[0]
apiError.AppendCause = true
}
return nil, apiError
}
sr, err := cert.ParseCsr([]byte(enrollData["serverCertCsr"].(string)))
if err != nil {
apiErr := apierror.NewCouldNotProcessCsr()
apiErr.Cause = err
apiErr.AppendCause = true
return nil, apiErr
}
so := &cert.SigningOpts{
DNSNames: sr.DNSNames,
EmailAddresses: sr.EmailAddresses,
IPAddresses: sr.IPAddresses,
URIs: sr.URIs,
}
srvCert, err := module.env.GetApiServerCsrSigner().Sign([]byte(enrollData["serverCertCsr"].(string)), so)
if err != nil {
return nil, apierror.NewCouldNotProcessCsr()
}
srvPem, err := module.env.GetApiServerCsrSigner().ToPem(srvCert)
srvChain := string(srvPem) + module.env.GetApiServerCsrSigner().SigningCertPEM()
if err != nil {
return nil, apierror.NewCouldNotProcessCsr()
}
cr, err := cert.ParseCsr([]byte(enrollData["certCsr"].(string)))
if err != nil {
return nil, apierror.NewCouldNotProcessCsr()
}
so = &cert.SigningOpts{
DNSNames: cr.DNSNames,
EmailAddresses: cr.EmailAddresses,
IPAddresses: cr.IPAddresses,
URIs: cr.URIs,
}
cr.Subject.CommonName = txRouter.Id
cltCert, err := module.env.GetControlClientCsrSigner().Sign([]byte(enrollData["certCsr"].(string)), so)
if err != nil {
return nil, apierror.NewCouldNotProcessCsr()
}
cltPem, err := module.env.GetControlClientCsrSigner().ToPem(cltCert)
if err != nil {
return nil, apierror.NewCouldNotProcessCsr()
}
cltFp := module.fingerprintGenerator.FromPem(cltPem)
txRouter.IsVerified = true
txRouter.Fingerprint = cltFp
if err := module.env.GetHandlers().TransitRouter.Update(txRouter); err != nil {
return nil, fmt.Errorf("could not update edge router: %s", err)
}
if err := module.env.GetHandlers().Enrollment.Delete(enrollment.Id); err != nil {
return nil, fmt.Errorf("could not delete enrollment: %s", err)
}
content, err := json.Marshal(&map[string]interface{}{
"serverCert": srvChain,
"cert": string(cltPem),
"ca": string(module.env.GetConfig().CaPems()),
})
if err != nil {
return nil, err
}
return &EnrollmentResult{
Identity: nil,
Authenticator: nil,
Content: content,
ContentType: "application/json",
Status: 200,
}, nil
}
+3 -3
View File
@@ -54,11 +54,11 @@ func (module *EnrollModuleUpdb) Process(ctx EnrollmentContext) (*EnrollmentResul
return nil, err
}
if enrollment == nil {
if enrollment == nil || enrollment.IdentityId == nil {
return nil, apierror.NewInvalidEnrollmentToken()
}
identity, err := module.env.GetHandlers().Identity.Read(enrollment.IdentityId)
identity, err := module.env.GetHandlers().Identity.Read(*enrollment.IdentityId)
if err != nil {
return nil, err
@@ -98,7 +98,7 @@ func (module *EnrollModuleUpdb) Process(ctx EnrollmentContext) (*EnrollmentResul
Id: uuid.New().String(),
},
Method: persistence.MethodAuthenticatorUpdb,
IdentityId: enrollment.IdentityId,
IdentityId: *enrollment.IdentityId,
SubType: &AuthenticatorUpdb{
Username: *enrollment.Username,
Password: encodedPassword,
+26 -20
View File
@@ -33,17 +33,19 @@ import (
type Enrollment struct {
models.BaseEntity
Method string
IdentityId string
Token string
IssuedAt *time.Time
ExpiresAt *time.Time
Jwt string
CaId *string
Username *string
Method string
IdentityId *string
TransitRouterId *string
EdgeRouterId *string
Token string
IssuedAt *time.Time
ExpiresAt *time.Time
Jwt string
CaId *string
Username *string
}
func (entity *Enrollment) FillJwtInfo(env Env) error {
func (entity *Enrollment) FillJwtInfo(env Env, subject string) error {
now := time.Now()
expiresAt := now.Add(env.GetConfig().Enrollment.EdgeIdentity.DurationMinutes)
entity.IssuedAt = &now
@@ -61,7 +63,7 @@ func (entity *Enrollment) FillJwtInfo(env Env) error {
Id: entity.Token,
Issuer: fmt.Sprintf("https://%s", env.GetConfig().Api.Advertise),
NotBefore: 0,
Subject: entity.IdentityId,
Subject: subject,
},
}
@@ -71,7 +73,7 @@ func (entity *Enrollment) FillJwtInfo(env Env) error {
return err
}
signedJwt, err := env.GetEnrollmentJwtGenerator().Generate(entity.IdentityId, entity.Id, mapClaims)
signedJwt, err := env.GetEnrollmentJwtGenerator().Generate(subject, entity.Id, mapClaims)
if err != nil {
return err
@@ -90,6 +92,8 @@ func (entity *Enrollment) fillFrom(_ Handler, _ *bbolt.Tx, boltEntity boltz.Enti
entity.FillCommon(boltEnrollment)
entity.Method = boltEnrollment.Method
entity.IdentityId = boltEnrollment.IdentityId
entity.TransitRouterId = boltEnrollment.TransitRouterId
entity.EdgeRouterId = boltEnrollment.EdgeRouterId
entity.CaId = boltEnrollment.CaId
entity.Username = boltEnrollment.Username
entity.Token = boltEnrollment.Token
@@ -120,15 +124,17 @@ func (entity *Enrollment) toBoltEntity(handler Handler) (boltz.Entity, error) {
}
boltEntity := &persistence.Enrollment{
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
Method: entity.Method,
IdentityId: entity.IdentityId,
Token: entity.Token,
IssuedAt: entity.IssuedAt,
ExpiresAt: entity.ExpiresAt,
Jwt: entity.Jwt,
CaId: entity.CaId,
Username: entity.Username,
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
Method: entity.Method,
IdentityId: entity.IdentityId,
EdgeRouterId: entity.EdgeRouterId,
TransitRouterId: entity.TransitRouterId,
Token: entity.Token,
IssuedAt: entity.IssuedAt,
ExpiresAt: entity.ExpiresAt,
Jwt: entity.Jwt,
CaId: entity.CaId,
Username: entity.Username,
}
return boltEntity, nil
+2
View File
@@ -33,6 +33,7 @@ type Handlers struct {
EdgeRouterPolicy *EdgeRouterPolicyHandler
EdgeService *EdgeServiceHandler
EventLog *EventLogHandler
TransitRouter *TransitRouterHandler
GeoRegion *GeoRegionHandler
Identity *IdentityHandler
IdentityType *IdentityTypeHandler
@@ -64,6 +65,7 @@ func InitHandlers(env Env) *Handlers {
handlers.Identity = NewIdentityHandler(env)
handlers.IdentityType = NewIdentityTypeHandler(env)
handlers.EdgeService = NewEdgeServiceHandler(env)
handlers.TransitRouter = NewTransitRouterHandler(env)
handlers.ServiceEdgeRouterPolicy = NewServiceEdgeRouterPolicyHandler(env)
handlers.ServicePolicy = NewServicePolicyHandler(env)
handlers.Session = NewSessionHandler(env)
+2 -2
View File
@@ -104,9 +104,9 @@ func (handler *IdentityHandler) CreateWithEnrollments(identityModel *Identity, e
}
for _, enrollmentModel := range enrollmentsModels {
enrollmentModel.IdentityId = identityModel.Id
enrollmentModel.IdentityId = &identityModel.Id
err := enrollmentModel.FillJwtInfo(handler.env)
err := enrollmentModel.FillJwtInfo(handler.env, identityModel.Id)
if err != nil {
return err
+162
View File
@@ -0,0 +1,162 @@
/*
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 model
import (
"github.com/google/uuid"
"github.com/michaelquigley/pfxlog"
"github.com/netfoundry/ziti-edge/controller/persistence"
"github.com/netfoundry/ziti-fabric/controller/models"
"github.com/netfoundry/ziti-foundation/storage/boltz"
"go.etcd.io/bbolt"
)
func NewTransitRouterHandler(env Env) *TransitRouterHandler {
handler := &TransitRouterHandler{
baseHandler: newBaseHandler(env, env.GetStores().TransitRouter),
}
handler.impl = handler
return handler
}
type TransitRouterHandler struct {
baseHandler
}
func (handler *TransitRouterHandler) Delete(id string) error {
return handler.deleteEntity(id)
}
func (handler *TransitRouterHandler) newModelEntity() boltEntitySink {
return &TransitRouter{}
}
func (handler *TransitRouterHandler) Create(entity *TransitRouter) (string, error) {
enrollment := &Enrollment{
BaseEntity: models.BaseEntity{},
Method: MethodEnrollTransitRouterOtt,
}
id, _, err := handler.CreateWithEnrollment(entity, enrollment)
return id, err
}
func (handler *TransitRouterHandler) CreateWithEnrollment(txRouter *TransitRouter, enrollment *Enrollment) (string, string, error) {
if txRouter.Id == "" {
txRouter.Id = uuid.New().String()
}
var enrollmentId string
err := handler.GetDb().Update(func(tx *bbolt.Tx) error {
ctx := boltz.NewMutateContext(tx)
boltEntity, err := txRouter.toBoltEntityForCreate(tx, handler.impl)
if err != nil {
return err
}
if err := handler.GetStore().Create(ctx, boltEntity); err != nil {
pfxlog.Logger().WithError(err).Errorf("could not create %v in bolt storage", handler.GetStore().GetSingularEntityType())
return err
}
enrollment.TransitRouterId = &txRouter.Id
err = enrollment.FillJwtInfo(handler.env, txRouter.Id)
if err != nil {
return err
}
enrollmentId, err = handler.env.GetHandlers().Enrollment.createEntityInTx(ctx, enrollment)
if err != nil {
return err
}
return nil
})
if err != nil {
return "", "", err
}
return txRouter.Id, enrollmentId, nil
}
func (handler *TransitRouterHandler) Update(entity *TransitRouter) error {
return handler.updateEntity(entity, nil)
}
func (handler *TransitRouterHandler) Patch(entity *TransitRouter, checker boltz.FieldChecker) error {
return handler.patchEntity(entity, checker)
}
func (handler *TransitRouterHandler) ReadOneByQuery(query string) (*TransitRouter, error) {
result, err := handler.readEntityByQuery(query)
if err != nil {
return nil, err
}
if result == nil {
return nil, nil
}
return result.(*TransitRouter), nil
}
func (handler *TransitRouterHandler) Read(id string) (*TransitRouter, error) {
result := &TransitRouter{}
if err := handler.readEntity(id, result); err != nil {
return nil, err
}
return result, nil
}
func (handler *TransitRouterHandler) readInTx(tx *bbolt.Tx, id string) (*TransitRouter, error) {
modelEntity := &TransitRouter{}
if err := handler.readEntityInTx(tx, id, modelEntity); err != nil {
return nil, err
}
return modelEntity, nil
}
func (handler *TransitRouterHandler) CollectEnrollments(id string, collector func(entity *Enrollment) error) error {
return handler.GetDb().View(func(tx *bbolt.Tx) error {
return handler.collectEnrollmentsInTx(tx, id, collector)
})
}
func (handler *TransitRouterHandler) collectEnrollmentsInTx(tx *bbolt.Tx, id string, collector func(entity *Enrollment) error) error {
_, err := handler.readInTx(tx, id)
if err != nil {
return err
}
associationIds := handler.GetStore().GetRelatedEntitiesIdList(tx, id, persistence.FieldTransitRouterEnrollments)
for _, enrollmentId := range associationIds {
enrollment, err := handler.env.GetHandlers().Enrollment.readInTx(tx, enrollmentId)
if err != nil {
return err
}
err = collector(enrollment)
if err != nil {
return err
}
}
return nil
}
+77
View File
@@ -0,0 +1,77 @@
/*
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 model
import (
"github.com/netfoundry/ziti-edge/controller/persistence"
"github.com/netfoundry/ziti-fabric/controller/db"
"github.com/netfoundry/ziti-fabric/controller/models"
"github.com/netfoundry/ziti-foundation/storage/boltz"
"github.com/pkg/errors"
"go.etcd.io/bbolt"
"reflect"
)
type TransitRouter struct {
models.BaseEntity
Name string
Fingerprint string
IsVerified bool
IsBase bool
}
func (entity *TransitRouter) toBoltEntityForCreate(tx *bbolt.Tx, handler Handler) (boltz.Entity, error) {
boltEntity := &persistence.TransitRouter{
Router: db.Router{
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
Fingerprint: entity.Fingerprint,
},
Name: entity.Name,
IsVerified: false,
}
return boltEntity, nil
}
func (entity *TransitRouter) toBoltEntityForUpdate(tx *bbolt.Tx, handler Handler) (boltz.Entity, error) {
ret := &persistence.TransitRouter{
Router: db.Router{
BaseExtEntity: *boltz.NewExtEntity(entity.Id, entity.Tags),
},
Name: entity.Name,
}
return ret, nil
}
func (entity *TransitRouter) toBoltEntityForPatch(tx *bbolt.Tx, handler Handler) (boltz.Entity, error) {
return entity.toBoltEntityForUpdate(tx, handler)
}
func (entity *TransitRouter) fillFrom(_ Handler, _ *bbolt.Tx, boltEntity boltz.Entity) error {
boltTransitRouter, ok := boltEntity.(*persistence.TransitRouter)
if !ok {
return errors.Errorf("unexpected type %v when filling model transitRouter", reflect.TypeOf(boltEntity))
}
entity.FillCommon(boltTransitRouter)
entity.Name = boltTransitRouter.Name
entity.IsVerified = boltTransitRouter.IsVerified
entity.IsBase = boltTransitRouter.IsBase
entity.Fingerprint = boltTransitRouter.Fingerprint
return nil
}
+1
View File
@@ -26,6 +26,7 @@ const (
EntityTypeConfigs = "configs"
EntityTypeConfigTypes = "configTypes"
EntityTypeEdgeRouters = "edgeRouters"
EntityTypeTransitRouters = "transitRouters"
EntityTypeEdgeRouterPolicies = "edgeRouterPolicies"
EntityTypeEventLogs = "eventLogs"
EntityTypeGeoRegions = "geoRegions"
+6
View File
@@ -64,6 +64,12 @@ func newChildBaseStore(stores *stores, parent boltz.CrudStore, entityType string
}
}
func newExtendedBaseStore(stores *stores, parent boltz.CrudStore, entityType string) *baseStore {
store := newChildBaseStore(stores, parent, entityType)
store.BaseStore.Extended()
return store
}
type baseStore struct {
stores *stores
*boltz.BaseStore
+27 -24
View File
@@ -17,6 +17,7 @@
package persistence
import (
"fmt"
"github.com/google/uuid"
"github.com/netfoundry/ziti-foundation/storage/ast"
"github.com/netfoundry/ziti-foundation/storage/boltz"
@@ -26,16 +27,15 @@ import (
)
const (
FieldEdgeRouterFingerprint = "fingerprint"
FieldEdgeRouterCertPEM = "certPem"
FieldEdgeRouterCluster = "cluster"
FieldEdgeRouterIsVerified = "isVerified"
FieldEdgeRouterEnrollmentToken = "enrollmentToken"
FieldEdgeRouterHostname = "hostname"
FieldEdgeRouterEnrollmentJwt = "enrollmentJwt"
FieldEdgeRouterEnrollmentCreatedAt = "enrollmentCreatedAt"
FieldEdgeRouterEnrollmentExpiresAt = "enrollmentExpiresAt"
FieldEdgeRouterProtocols = "protocols"
FieldEdgeRouterFingerprint = "fingerprint"
FieldEdgeRouterCertPEM = "certPem"
FieldEdgeRouterCluster = "cluster"
FieldEdgeRouterIsVerified = "isVerified"
FieldEdgeRouterHostname = "hostname"
FieldEdgeRouterProtocols = "protocols"
FieldEdgeRouterEnrollments = "enrollments"
MethodEnrollEdgeRouterOtt = "erott"
)
func newEdgeRouter(name string, roleAttributes ...string) *EdgeRouter {
@@ -53,13 +53,16 @@ type EdgeRouter struct {
IsVerified bool
Fingerprint *string
CertPem *string
EnrollmentToken *string
Hostname *string
EdgeRouterProtocols map[string]string
RoleAttributes []string
Enrollments []string
//old v4, migrations only
EnrollmentToken *string
EnrollmentJwt *string
EnrollmentCreatedAt *time.Time
EnrollmentExpiresAt *time.Time
EdgeRouterProtocols map[string]string
RoleAttributes []string
}
var edgeRouterFieldMappings = map[string]string{FieldEdgeRouterCluster: "clusterId"}
@@ -72,11 +75,9 @@ func (entity *EdgeRouter) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucke
entity.ClusterId = bucket.GetString(FieldEdgeRouterCluster)
entity.IsVerified = bucket.GetBoolWithDefault(FieldEdgeRouterIsVerified, false)
entity.EnrollmentToken = bucket.GetString(FieldEdgeRouterEnrollmentToken)
//old v4, migrations only
entity.Enrollments = bucket.GetStringList(FieldEdgeRouterEnrollments)
entity.Hostname = bucket.GetString(FieldEdgeRouterHostname)
entity.EnrollmentJwt = bucket.GetString(FieldEdgeRouterEnrollmentJwt)
entity.EnrollmentCreatedAt = bucket.GetTime(FieldEdgeRouterEnrollmentCreatedAt)
entity.EnrollmentExpiresAt = bucket.GetTime(FieldEdgeRouterEnrollmentExpiresAt)
entity.EdgeRouterProtocols = toStringStringMap(bucket.GetMap(FieldEdgeRouterProtocols))
entity.RoleAttributes = bucket.GetStringList(FieldRoleAttributes)
}
@@ -91,13 +92,10 @@ func (entity *EdgeRouter) SetValues(ctx *boltz.PersistContext) {
ctx.SetStringP(FieldEdgeRouterCertPEM, entity.CertPem)
ctx.SetStringP(FieldEdgeRouterCluster, entity.ClusterId)
ctx.SetBool(FieldEdgeRouterIsVerified, entity.IsVerified)
ctx.SetStringP(FieldEdgeRouterEnrollmentToken, entity.EnrollmentToken)
ctx.SetStringP(FieldEdgeRouterHostname, entity.Hostname)
ctx.SetStringP(FieldEdgeRouterEnrollmentJwt, entity.EnrollmentJwt)
ctx.SetTimeP(FieldEdgeRouterEnrollmentCreatedAt, entity.EnrollmentCreatedAt)
ctx.SetTimeP(FieldEdgeRouterEnrollmentExpiresAt, entity.EnrollmentExpiresAt)
ctx.SetMap(FieldEdgeRouterProtocols, toStringInterfaceMap(entity.EdgeRouterProtocols))
ctx.SetStringList(FieldRoleAttributes, entity.RoleAttributes)
ctx.SetLinkedIds(FieldEdgeRouterEnrollments, entity.Enrollments)
// index change won't fire if we don't have any roles on create, but we need to evaluate if we match any #all roles
if ctx.IsCreate && len(entity.RoleAttributes) == 0 {
@@ -137,6 +135,7 @@ type edgeRouterStoreImpl struct {
symbolEdgeRouterPolicies boltz.EntitySetSymbol
symbolServiceEdgeRouterPolicies boltz.EntitySetSymbol
symbolEnrollments boltz.EntitySetSymbol
}
func (store *edgeRouterStoreImpl) NewStoreEntity() boltz.Entity {
@@ -155,9 +154,7 @@ func (store *edgeRouterStoreImpl) initializeLocal() {
store.AddSymbol(FieldEdgeRouterFingerprint, ast.NodeTypeString)
store.AddSymbol(FieldEdgeRouterIsVerified, ast.NodeTypeBool)
store.AddSymbol(FieldEdgeRouterEnrollmentToken, ast.NodeTypeString)
store.AddSymbol(FieldEdgeRouterEnrollmentCreatedAt, ast.NodeTypeString)
store.AddSymbol(FieldEdgeRouterEnrollmentExpiresAt, ast.NodeTypeString)
store.symbolEnrollments = store.AddFkSetSymbol(FieldEdgeRouterEnrollments, store.stores.enrollment)
store.symbolEdgeRouterPolicies = store.AddFkSetSymbol(EntityTypeEdgeRouterPolicies, store.stores.edgeRouterPolicy)
store.symbolServiceEdgeRouterPolicies = store.AddFkSetSymbol(EntityTypeServiceEdgeRouterPolicies, store.stores.serviceEdgeRouterPolicy)
@@ -181,6 +178,7 @@ func (store *edgeRouterStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []b
func (store *edgeRouterStoreImpl) initializeLinked() {
store.AddLinkCollection(store.symbolEdgeRouterPolicies, store.stores.edgeRouterPolicy.symbolEdgeRouters)
store.AddLinkCollection(store.symbolServiceEdgeRouterPolicies, store.stores.serviceEdgeRouterPolicy.symbolEdgeRouters)
store.AddLinkCollection(store.symbolEnrollments, store.stores.enrollment.symbolEdgeRouter)
}
func (store *edgeRouterStoreImpl) GetNameIndex() boltz.ReadIndex {
@@ -213,6 +211,11 @@ func (store *edgeRouterStoreImpl) DeleteById(ctx boltz.MutateContext, id string)
if err := store.deleteEntityReferences(ctx.Tx(), entity, store.stores.serviceEdgeRouterPolicy.symbolEdgeRouterRoles); err != nil {
return err
}
// Remove outstanding enrollments
if err := store.stores.enrollment.DeleteWhere(ctx, fmt.Sprintf(`edgeRouter="%s"`, entity.Id)); err != nil {
return err
}
}
if store.stores.Router.IsEntityPresent(ctx.Tx(), id) {
+34 -16
View File
@@ -27,6 +27,8 @@ const (
FieldEnrollmentToken = "token"
FieldEnrollmentMethod = "method"
FieldEnrollIdentity = "identity"
FieldEnrollEdgeRouter = "edgeRouter"
FieldEnrollTransitRouter = "transitRouter"
FieldEnrollmentExpiresAt = "expiresAt"
FieldEnrollmentIssuedAt = "issuedAt"
FieldEnrollmentCaId = "caId"
@@ -41,22 +43,30 @@ const (
type Enrollment struct {
boltz.BaseExtEntity
Token string
Method string
IdentityId string
ExpiresAt *time.Time
IssuedAt *time.Time
CaId *string
Username *string
Jwt string
Token string
Method string
IdentityId *string
TransitRouterId *string
EdgeRouterId *string
ExpiresAt *time.Time
IssuedAt *time.Time
CaId *string
Username *string
Jwt string
}
var enrollmentFieldMappings = map[string]string{FieldEnrollIdentity: "identityId"}
var enrollmentFieldMappings = map[string]string{
FieldEnrollIdentity: "identityId",
FieldEnrollEdgeRouter: "edgeRouterId",
FieldEnrollTransitRouter: "transitRouterId",
}
func (entity *Enrollment) LoadValues(_ boltz.CrudStore, bucket *boltz.TypedBucket) {
entity.Token = bucket.GetStringWithDefault(FieldEnrollmentToken, "")
entity.Method = bucket.GetStringWithDefault(FieldEnrollmentMethod, "")
entity.IdentityId = bucket.GetStringOrError(FieldEnrollIdentity)
entity.IdentityId = bucket.GetString(FieldEnrollIdentity)
entity.EdgeRouterId = bucket.GetString(FieldEnrollEdgeRouter)
entity.TransitRouterId = bucket.GetString(FieldEnrollTransitRouter)
entity.ExpiresAt = bucket.GetTime(FieldEnrollmentExpiresAt)
entity.IssuedAt = bucket.GetTime(FieldEnrollmentIssuedAt)
entity.CaId = bucket.GetString(FieldEnrollmentCaId)
@@ -70,7 +80,9 @@ func (entity *Enrollment) SetValues(ctx *boltz.PersistContext) {
ctx.SetString(FieldEnrollmentToken, entity.Token)
ctx.SetString(FieldEnrollmentMethod, entity.Method)
ctx.SetTimeP(FieldEnrollmentExpiresAt, entity.ExpiresAt)
ctx.SetString(FieldEnrollIdentity, entity.IdentityId)
ctx.SetStringP(FieldEnrollIdentity, entity.IdentityId)
ctx.SetStringP(FieldEnrollEdgeRouter, entity.EdgeRouterId)
ctx.SetStringP(FieldEnrollTransitRouter, entity.TransitRouterId)
ctx.SetStringP(FieldEnrollmentCaId, entity.CaId)
ctx.SetStringP(FieldEnrollmentUsername, entity.Username)
ctx.SetTimeP(FieldEnrollmentIssuedAt, entity.IssuedAt)
@@ -99,8 +111,10 @@ func newEnrollmentStore(stores *stores) *enrollmentStoreImpl {
type enrollmentStoreImpl struct {
*baseStore
tokenIndex boltz.ReadIndex
symbolIdentityId boltz.EntitySymbol
tokenIndex boltz.ReadIndex
symbolIdentity boltz.EntitySymbol
symbolEdgeRouter boltz.EntitySymbol
symbolTransitRouter boltz.EntitySymbol
}
func (store *enrollmentStoreImpl) NewStoreEntity() boltz.Entity {
@@ -109,14 +123,18 @@ func (store *enrollmentStoreImpl) NewStoreEntity() boltz.Entity {
func (store *enrollmentStoreImpl) initializeLocal() {
store.AddExtEntitySymbols()
symbolToken := store.AddSymbol(FieldEnrollmentToken, ast.NodeTypeString)
store.tokenIndex = store.AddUniqueIndex(symbolToken)
store.symbolIdentityId = store.AddFkSymbol(FieldEnrollIdentity, store.stores.identity)
store.symbolIdentity = store.AddFkSymbol(FieldEnrollIdentity, store.stores.identity)
store.symbolEdgeRouter = store.AddFkSymbol(FieldEnrollEdgeRouter, store.stores.edgeRouter)
store.symbolTransitRouter = store.AddFkSymbol(FieldEnrollTransitRouter, store.stores.transitRouter)
}
func (store *enrollmentStoreImpl) initializeLinked() {
store.AddFkIndex(store.symbolIdentityId, store.stores.identity.symbolEnrollments)
store.AddNullableFkIndex(store.symbolIdentity, store.stores.identity.symbolEnrollments)
store.AddNullableFkIndex(store.symbolEdgeRouter, store.stores.edgeRouter.symbolEnrollments)
store.AddNullableFkIndex(store.symbolTransitRouter, store.stores.transitRouter.symbolEnrollments)
}
func (store *enrollmentStoreImpl) LoadOneById(tx *bbolt.Tx, id string) (*Enrollment, error) {
+1 -1
View File
@@ -179,7 +179,7 @@ func (store *identityStoreImpl) rolesChanged(tx *bbolt.Tx, rowId []byte, _ []bol
func (store *identityStoreImpl) initializeLinked() {
store.AddLinkCollection(store.symbolAuthenticators, store.stores.authenticator.symbolIdentityId)
store.AddLinkCollection(store.symbolEnrollments, store.stores.enrollment.symbolIdentityId)
store.AddLinkCollection(store.symbolEnrollments, store.stores.enrollment.symbolIdentity)
store.AddLinkCollection(store.symbolEdgeRouterPolicies, store.stores.edgeRouterPolicy.symbolIdentities)
store.AddLinkCollection(store.symbolServicePolicies, store.stores.servicePolicy.symbolIdentities)
}
+55
View File
@@ -0,0 +1,55 @@
/*
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 persistence
import (
"github.com/netfoundry/ziti-foundation/storage/boltz"
)
func (m *Migrations) createEnrollmentsForEdgeRouters(step *boltz.MigrationStep) {
edgeRouterIds, _, err := m.stores.EdgeRouter.QueryIds(step.Ctx.Tx(), "true")
step.SetError(err)
for _, edgeRouterId := range edgeRouterIds {
edgeRouter, err := m.stores.EdgeRouter.LoadOneById(step.Ctx.Tx(), edgeRouterId)
if step.SetError(err) {
return
}
if edgeRouter.EnrollmentJwt == nil {
continue
}
enrollment := &Enrollment{
Token: *edgeRouter.EnrollmentToken,
Method: MethodEnrollEdgeRouterOtt,
EdgeRouterId: &edgeRouter.Id,
ExpiresAt: edgeRouter.EnrollmentExpiresAt,
IssuedAt: edgeRouter.EnrollmentCreatedAt,
Jwt: *edgeRouter.EnrollmentJwt,
}
step.SetError(m.stores.Enrollment.Create(step.Ctx, enrollment))
edgeRouter.EnrollmentJwt = nil
edgeRouter.EnrollmentCreatedAt = nil
edgeRouter.EnrollmentExpiresAt = nil
edgeRouter.EnrollmentToken = nil
step.SetError(m.stores.EdgeRouter.Update(step.Ctx, edgeRouter, nil))
}
}
+6 -2
View File
@@ -63,9 +63,13 @@ func (m *Migrations) migrate(step *boltz.MigrationStep) int {
return 4
}
// current version
if step.CurrentVersion == 4 {
return 4
m.createEnrollmentsForEdgeRouters(step)
return 5
}
// current version
if step.CurrentVersion == 5 {
return 5
}
step.SetError(errors.Errorf("Unsupported edge datastore version: %v", step.CurrentVersion))
+1 -1
View File
@@ -149,7 +149,7 @@ func (m *Migrations) migrateEnrollmentsFromPG(step *boltz.MigrationStep) error {
BaseExtEntity: *toBaseBoltEntity(&pgEnrollment.BaseDbEntity),
Token: *pgEnrollment.Token,
Method: *pgEnrollment.Method,
IdentityId: *pgEnrollment.IdentityID,
IdentityId: pgEnrollment.IdentityID,
ExpiresAt: pgEnrollment.ExpiresAt,
IssuedAt: pgEnrollment.CreatedAt,
CaId: nil,
+4
View File
@@ -51,6 +51,7 @@ type Stores struct {
Session SessionStore
ServiceEdgeRouterPolicy ServiceEdgeRouterPolicyStore
ServicePolicy ServicePolicyStore
TransitRouter TransitRouterStore
Enrollment EnrollmentStore
Authenticator AuthenticatorStore
@@ -108,6 +109,7 @@ type stores struct {
serviceEdgeRouterPolicy *serviceEdgeRouterPolicyStoreImpl
servicePolicy *servicePolicyStoreImpl
session *sessionStoreImpl
transitRouter *transitRouterStoreImpl
enrollment *enrollmentStoreImpl
authenticator *authenticatorStoreImpl
}
@@ -134,6 +136,7 @@ func NewBoltStores(dbProvider DbProvider) (*Stores, error) {
internalStores.edgeRouterPolicy = newEdgeRouterPolicyStore(internalStores)
internalStores.edgeService = newEdgeServiceStore(internalStores)
internalStores.eventLog = newEventLogStore(internalStores)
internalStores.transitRouter = newTransitRouterStore(internalStores)
internalStores.geoRegion = newGeoRegionStore(internalStores)
internalStores.identity = newIdentityStore(internalStores)
internalStores.identityType = newIdentityTypeStore(internalStores)
@@ -159,6 +162,7 @@ func NewBoltStores(dbProvider DbProvider) (*Stores, error) {
EdgeRouterPolicy: internalStores.edgeRouterPolicy,
EdgeService: internalStores.edgeService,
EventLog: internalStores.eventLog,
TransitRouter: internalStores.transitRouter,
GeoRegion: internalStores.geoRegion,
Identity: internalStores.identity,
IdentityType: internalStores.identityType,
@@ -0,0 +1,183 @@
/*
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 persistence
import (
"fmt"
"github.com/google/uuid"
"github.com/netfoundry/ziti-fabric/controller/db"
"github.com/netfoundry/ziti-foundation/storage/boltz"
"go.etcd.io/bbolt"
)
const (
FieldTransitRouterIsVerified = "isVerified"
FieldTransitRouterEnrollments = "enrollments"
//old
FieldTransitRouterEnrollmentToken = "enrollmentToken"
FieldTransitRouterEnrollmentJwt = "enrollmentJwt"
FieldTransitRouterEnrollmentCreatedAt = "enrollmentCreatedAt"
FieldTransitRouterEnrollmentExpiresAt = "enrollmentExpiresAt"
)
type TransitRouter struct {
db.Router
Name string
IsVerified bool
Enrollments []string
IsBase bool
}
func (entity *TransitRouter) GetId() string {
return entity.Id
}
func (entity *TransitRouter) SetId(id string) {
entity.Id = id
}
func newTransitRouter(name string, roleAttributes ...string) *TransitRouter {
return &TransitRouter{
Router: db.Router{
BaseExtEntity: boltz.BaseExtEntity{Id: uuid.New().String()},
},
Name: name,
}
}
func (entity *TransitRouter) LoadValues(store boltz.CrudStore, bucket *boltz.TypedBucket) {
_, err := store.GetParentStore().BaseLoadOneById(bucket.Tx(), entity.Id, &entity.Router)
bucket.SetError(err)
if bucket.Bucket == nil {
entity.IsVerified = true
entity.IsBase = true
return
}
entity.LoadBaseValues(bucket)
entity.Name = bucket.GetStringOrError(FieldName)
entity.IsVerified = bucket.GetBoolWithDefault(FieldTransitRouterIsVerified, false)
entity.Enrollments = bucket.GetStringList(FieldTransitRouterEnrollments)
}
func (entity *TransitRouter) SetValues(ctx *boltz.PersistContext) {
entity.Router.SetValues(ctx.GetParentContext())
entity.SetBaseValues(ctx)
ctx.SetString(FieldName, entity.Name)
}
func (entity *TransitRouter) GetEntityType() string {
return EntityTypeTransitRouters
}
func (entity *TransitRouter) GetName() string {
return entity.Name
}
type TransitRouterStore interface {
NameIndexedStore
LoadOneById(tx *bbolt.Tx, id string) (*TransitRouter, error)
LoadOneByName(tx *bbolt.Tx, id string) (*TransitRouter, error)
}
func newTransitRouterStore(stores *stores) *transitRouterStoreImpl {
store := &transitRouterStoreImpl{
baseStore: newExtendedBaseStore(stores, stores.Router, EntityTypeTransitRouters),
}
store.InitImpl(store)
return store
}
type transitRouterStoreImpl struct {
*baseStore
indexName boltz.ReadIndex
symbolEnrollments boltz.EntitySetSymbol
}
func (store *transitRouterStoreImpl) NewStoreEntity() boltz.Entity {
return &TransitRouter{}
}
func (store *transitRouterStoreImpl) initializeLocal() {
store.AddExtEntitySymbols()
store.GetParentStore().GrantSymbols(store)
store.indexName = store.addUniqueNameField()
store.symbolEnrollments = store.AddFkSetSymbol(FieldTransitRouterEnrollments, store.stores.enrollment)
}
func (store *transitRouterStoreImpl) initializeLinked() {
store.AddLinkCollection(store.symbolEnrollments, store.stores.enrollment.symbolIdentity)
}
func (store *transitRouterStoreImpl) CleanupExternal(ctx boltz.MutateContext, id string) error {
entity, err := store.LoadOneById(ctx.Tx(), id)
if err != nil {
return err
}
//no edge cleanup
if entity.IsBase {
return nil
}
//edge cleanup
if err = store.stores.enrollment.DeleteWhere(ctx, fmt.Sprintf(`transitRouter="%s"`, id)); err != nil {
return nil
}
return store.BaseStore.CleanupExternal(ctx, id)
}
func (store *transitRouterStoreImpl) GetNameIndex() boltz.ReadIndex {
return store.indexName
}
func (store *transitRouterStoreImpl) LoadOneById(tx *bbolt.Tx, id string) (*TransitRouter, error) {
entity := &TransitRouter{}
if err := store.baseLoadOneById(tx, id, entity); err != nil {
return nil, err
}
return entity, nil
}
func (store *transitRouterStoreImpl) LoadOneByName(tx *bbolt.Tx, name string) (*TransitRouter, error) {
id := store.indexName.Read(tx, []byte(name))
if id != nil {
return store.LoadOneById(tx, string(id))
}
return nil, nil
}
func (store *transitRouterStoreImpl) LoadOneByQuery(tx *bbolt.Tx, query string) (*TransitRouter, error) {
entity := &TransitRouter{}
if found, err := store.BaseLoadOneByQuery(tx, query, entity); !found || err != nil {
return nil, err
}
return entity, nil
}
func (store *transitRouterStoreImpl) DeleteById(ctx boltz.MutateContext, id string) error {
if entity, _ := store.LoadOneById(ctx.Tx(), id); entity != nil {
// Remove outstanding enrollments
if err := store.stores.enrollment.DeleteWhere(ctx, fmt.Sprintf(`transitRouter="%s"`, entity.Id)); err != nil {
return err
}
}
return store.BaseStore.DeleteById(ctx, id)
}
+7 -8
View File
@@ -20,7 +20,6 @@ import (
"fmt"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/netfoundry/ziti-edge/controller/apierror"
"github.com/netfoundry/ziti-edge/controller/model"
"net/http"
)
@@ -59,13 +58,13 @@ func (rc *RequestContext) GetIdFromRequest(idType IdType) (string, error) {
return "", fmt.Errorf("id property '%s' not found in request", IdPropertyName)
}
if idType == IdTypeUuid {
_, err := uuid.Parse(id)
if err != nil {
return "", apierror.NewInvalidUuid(id)
}
}
//if idType == IdTypeUuid {
// _, err := uuid.Parse(id)
//
// if err != nil {
// return "", apierror.NewInvalidUuid(id)
// }
//}
return id, nil
}
+2 -1
View File
@@ -188,7 +188,8 @@ func (c *Controller) initializeAuthModules() {
c.AppEnv.EnrollRegistry.Add(model.NewEnrollModuleCa(c.AppEnv))
c.AppEnv.EnrollRegistry.Add(model.NewEnrollModuleOttCa(c.AppEnv))
c.AppEnv.EnrollRegistry.Add(model.NewEnrollModuleOtt(c.AppEnv))
c.AppEnv.EnrollRegistry.Add(model.NewEnrollModuleEr(c.AppEnv))
c.AppEnv.EnrollRegistry.Add(model.NewEnrollModuleEdgeRouterOtt(c.AppEnv))
c.AppEnv.EnrollRegistry.Add(model.NewEnrollModuleTransitRouterOtt(c.AppEnv))
c.AppEnv.EnrollRegistry.Add(model.NewEnrollModuleUpdb(c.AppEnv))
})
}
+1 -1
View File
@@ -60,7 +60,7 @@ func NewRestEnroller() Enroller {
func (re *RestEnroller) parseCfgMap(cfgmap map[interface{}]interface{}) (*gateway.Config, error) {
config := gateway.NewConfig()
if err := config.LoadConfigFromMap(cfgmap); err != nil {
if err := config.LoadConfigFromMapForEnrollment(cfgmap); err != nil {
return nil, err
}
+36 -6
View File
@@ -53,6 +53,32 @@ func NewConfig() *Config {
return &Config{}
}
func (config *Config) LoadConfigFromMapForEnrollment(configMap map[interface{}]interface{}) error {
//enrollment config loading is more lax on where the CSR section lives (i.e. under edge: or at the root level)
if val, ok := configMap["edge"]; ok && val != nil {
var edgeConfigMap map[interface{}]interface{} = nil
config.Enabled = true
if edgeConfigMap, ok = val.(map[interface{}]interface{}); !ok {
return fmt.Errorf("expected map as edge configuration")
}
if err := config.loadCsr(edgeConfigMap, "edge"); err != nil {
return err
}
} else if val, ok := configMap["csr"]; ok && val != nil {
if err := config.loadCsr(configMap, ""); err != nil {
return err
}
} else {
return fmt.Errorf("expected enrollment CSR section")
}
config.loadIdentity(configMap)
return nil
}
func (config *Config) LoadConfigFromMap(configMap map[interface{}]interface{}) error {
var err error
config.Enabled = false
@@ -72,7 +98,7 @@ func (config *Config) LoadConfigFromMap(configMap map[interface{}]interface{}) e
return err
}
if err = config.loadCsr(edgeConfigMap); err != nil {
if err = config.loadCsr(edgeConfigMap, "edge"); err != nil {
return err
}
@@ -186,22 +212,26 @@ func (config *Config) loadListener(rootConfigMap map[interface{}]interface{}) er
return nil
}
func (config *Config) loadCsr(edgeConfigMap map[interface{}]interface{}) error {
func (config *Config) loadCsr(configMap map[interface{}]interface{}, pathPrefix string) error {
config.Csr = Csr{}
if value, found := edgeConfigMap["csr"]; found {
if pathPrefix != "" {
pathPrefix = pathPrefix + "."
}
if value, found := configMap["csr"]; found {
submap := value.(map[interface{}]interface{})
if submap == nil {
return errors.New("required section [edge.csr] is not a map")
return fmt.Errorf("required section [%scsr] is not a map", pathPrefix)
}
if err := mapstructure.Decode(submap, &config.Csr); err != nil {
return fmt.Errorf("failed to load [edge.csr]: %s", err)
return fmt.Errorf("failed to load [%scsr]: %s", pathPrefix, err)
}
} else {
return errors.New("required section [edge.csr] not found")
return fmt.Errorf("required section [%scsr] not found", pathPrefix)
}
for _, uristr := range config.Csr.Sans.UriAddresses {
+206 -31
View File
@@ -2488,6 +2488,133 @@ paths:
'409':
$ref: '#/responses/cannotDeleteReferencedResourceResponse'
###################################################################
# Transit Routers
##################################################################
'/transit-routers':
get:
summary: List transit routers
description: |
Retrieves a list of transit router resources; supports filtering, sorting, and pagination. Requires admin access.
security:
- ztSession: []
tags:
- Transit Router
operationId: listTransitRouters
parameters:
- $ref: '#/parameters/limit'
- $ref: '#/parameters/offset'
- $ref: '#/parameters/filter'
responses:
'200':
$ref: '#/responses/listTransitRouters'
'401':
$ref: '#/responses/unauthorizedResponse'
post:
summary: Create a transit router resource
description: Create a transit router resource. Requires admin access.
security:
- ztSession: []
tags:
- Transit Router
operationId: createTransitRouter
parameters:
- name: Body
in: body
required: true
description: A transit router to create
schema:
$ref: '#/definitions/transitRouterCreate'
responses:
'200':
$ref: '#/responses/createResponse'
'400':
$ref: '#/responses/badRequestResponse'
'401':
$ref: '#/responses/unauthorizedResponse'
'/transit-routers/{id}':
parameters:
- $ref: '#/parameters/id'
get:
summary: Retrieves a single transit router
description: Retrieves a single transit router by id. Requires admin access.
security:
- ztSession: []
tags:
- Transit Router
operationId: detailTransitRouter
responses:
'200':
$ref: '#/responses/detailTransitRouter'
'404':
$ref: '#/responses/notFoundResponse'
'401':
$ref: '#/responses/unauthorizedResponse'
put:
summary: Update all fields on a transit router
description: Update all fields on a transit router by id. Requires admin access.
security:
- ztSession: []
tags:
- Transit Router
operationId: updateTransitRouter
parameters:
- name: Body
in: body
required: true
description: A transit router update object
schema:
$ref: '#/definitions/transitRouterUpdate'
responses:
'200':
$ref: '#/responses/updateResponse'
'400':
$ref: '#/responses/badRequestResponse'
'404':
$ref: '#/responses/notFoundResponse'
'401':
$ref: '#/responses/unauthorizedResponse'
patch:
summary: Update the supplied fields on a transit router
description: Update the supplied fields on a transit router. Requires admin access.
security:
- ztSession: []
tags:
- Transit Router
operationId: patchTransitRouter
parameters:
- name: Body
in: body
required: true
description: A transit router patch object
schema:
$ref: '#/definitions/transitRouterPatch'
responses:
'200':
$ref: '#/responses/patchResponse'
'400':
$ref: '#/responses/badRequestResponse'
'404':
$ref: '#/responses/notFoundResponse'
'401':
$ref: '#/responses/unauthorizedResponse'
delete:
summary: Delete a transit router
description: Delete a transit router by id. Requires admin access.
security:
- ztSession: []
tags:
- Transit Router
operationId: deleteTransitRouter
responses:
'200':
$ref: '#/responses/deleteResponse'
'400':
$ref: '#/responses/badRequestResponse'
'401':
$ref: '#/responses/unauthorizedResponse'
'409':
$ref: '#/responses/cannotDeleteReferencedResourceResponse'
#######################################################################################################################
#
# Parameters - Reusable parameters
@@ -2962,6 +3089,17 @@ responses:
description: Returns the document that represents the specification
schema:
$ref: '#/definitions/detailSpecBodyEnvelope'
###################################################################
# Transit Routers
##################################################################
listTransitRouters:
description: A list of specifications
schema:
$ref: '#/definitions/listTransitRoutersEnvelope'
detailTransitRouter:
description: A single transit router
schema:
$ref: '#/definitions/detailTransitRouterEnvelope'
#######################################################################################################################
#
# Definitions - In & Out Models Only
@@ -3050,31 +3188,6 @@ definitions:
$ref: '#/definitions/sdkInfo'
configTypes:
$ref: '#/definitions/configTypes'
authenticateUPDB:
type: object
description: A username and password pair
required: [username, password]
properties:
username:
$ref: '#/definitions/username'
password:
$ref: '#/definitions/password'
envInfo:
$ref: '#/definitions/envInfo'
sdkInfo:
$ref: '#/definitions/sdkInfo'
configTypes:
$ref: '#/definitions/configTypes'
authenticateCert:
type: object
description: A cert based authentication request. The certificate used by the client as its TLS client certificate will be interrogated for authentication
properties:
envInfo:
$ref: '#/definitions/envInfo'
sdkInfo:
$ref: '#/definitions/sdkInfo'
configTypes:
$ref: '#/definitions/configTypes'
###################################################################
# Current Session
##################################################################
@@ -4694,17 +4807,17 @@ definitions:
tags:
$ref: '#/definitions/tags'
serviceId:
type: "string"
type: string
service:
$ref: '#/definitions/entityRef'
routerId:
type: "string"
type: string
router:
$ref: '#/definitions/entityRef'
binding:
type: "string"
type: string
address:
type: "string"
type: string
terminatorCreate:
type: object
required:
@@ -4798,7 +4911,69 @@ definitions:
tags:
$ref: '#/definitions/tags'
name:
type: "string"
type: string
specBodyDetail:
type: string
###################################################################
# Transit Routers
##################################################################
listTransitRoutersEnvelope:
type: object
properties:
meta:
$ref: '#/definitions/meta'
data:
$ref: '#/definitions/transitRouterList'
detailTransitRouterEnvelope:
type: object
properties:
meta:
$ref: '#/definitions/meta'
data:
$ref: '#/definitions/transitRouterDetail'
transitRouterList:
type: array
items:
$ref: '#/definitions/transitRouterDetail'
transitRouterDetail:
type: object
properties:
id:
type: string
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
_links:
$ref: '#/definitions/links'
tags:
$ref: '#/definitions/tags'
name:
type: string
fingerprint:
type: string
isVerified:
type: boolean
isOnline:
type: boolean
transitRouterCreate:
type: object
required:
- name
properties:
name:
type: string
transitRouterUpdate:
type: object
required:
- name
properties:
name:
type: string
transitRouterPatch:
type: object
properties:
name:
type: string
+62
View File
@@ -0,0 +1,62 @@
v: 2
# (required) The certificates, ca chains, and private key used to represent this routers identity
#
# If the 'edge" section is enabled and enrollment is run, the files in this section will be written to with the results.
# The "key" value can be provided through any of the support private key engines (file, etc.).
#
# If the 'key' value is a file and that file exists; it is expected to a pre-generated private key. It will be used for
# the enrollment process and it will back the certificates generated during enrollment. If the file does not exist, it
# will be generated.
#
# The 'cert', 'server_cert', and 'ca' files will be overwritten with the results of the enrollment process.
#
# When running the ziti-router the files/values in this section will be read and used. The 'cert' value will be used
# as the client certificate to connect to the controller. The 'server_cert' is used for all servers/listeners. The
# 'ca' value is used to populate an internally Trusted CA pool.
identity:
cert: testdata/transit-router/transit-router-client.cert.pem
server_cert: testdata/transit-router/transit-router-server.cert.pem
key: testdata/transit-router/transit-router.key.pem
ca: testdata/transit-router/transit-router-ca-chain.cert.pem
#trace:
# path: 001.trace
#profile:
# memory:
# path: 001.memprof
# cpu:
# path: 001.cpuprof
ctrl:
endpoint: tls:127.0.0.1:6262
csr:
country: US
province: NC
locality: Charlotte
organization: NetFoundry
organizationalUnit: Ziti
sans:
dns:
- "localhost"
- "test-network"
- "test-network.localhost"
- "ziti-dev-ingress01"
email:
- "admin@example.com"
ip:
- "127.0.0.1"
uri:
- "ziti://ziti-dev-gateway01/made/up/example"
dialers:
- binding: udp
- binding: transport
listeners:
- binding: transport
address: tls:0.0.0.0:7099
+39 -27
View File
@@ -239,9 +239,9 @@ func (request *authenticatedRequests) requireCreateIdentity(name string, isAdmin
request.testContext.setJsonValue(entityData, enrollments, "enrollment")
entityJson := entityData.String()
httpCode, body := request.createEntityOfType("identities", entityJson)
request.testContext.req.Equal(http.StatusCreated, httpCode)
id := request.testContext.getEntityId(body)
resp := request.createEntityOfType("identities", entityJson)
request.testContext.req.Equal(http.StatusCreated, resp.StatusCode())
id := request.testContext.getEntityId(resp.Body())
return id
}
@@ -263,9 +263,9 @@ func (request *authenticatedRequests) requireCreateIdentityWithUpdbEnrollment(na
request.testContext.setJsonValue(entityData, enrollments, "enrollment")
entityJson := entityData.String()
httpCode, body := request.createEntityOfType("identities", entityJson)
request.testContext.req.Equal(http.StatusCreated, httpCode)
id := request.testContext.getEntityId(body)
resp := request.createEntityOfType("identities", entityJson)
request.testContext.req.Equal(http.StatusCreated, resp.StatusCode())
id := request.testContext.getEntityId(resp.Body())
request.testContext.completeUpdbEnrollment(id, password)
return id, userAuth
}
@@ -283,9 +283,9 @@ func (request *authenticatedRequests) requireCreateIdentityOttEnrollment(name st
request.testContext.setJsonValue(entityData, enrollments, "enrollment")
entityJson := entityData.String()
httpCode, body := request.createEntityOfType("identities", entityJson)
request.testContext.req.Equal(http.StatusCreated, httpCode)
id := request.testContext.getEntityId(body)
resp := request.createEntityOfType("identities", entityJson)
request.testContext.req.Equal(http.StatusCreated, resp.StatusCode())
id := request.testContext.getEntityId(resp.Body())
return id, request.testContext.completeOttEnrollment(id)
}
@@ -307,6 +307,12 @@ func (request *authenticatedRequests) requireNewEdgeRouter(roleAttributes ...str
return edgeRouter
}
func (request *authenticatedRequests) requireNewTransitRouter() *transitRouter {
transitRouter := newTestTransitRouter()
request.requireCreateEntity(transitRouter)
return transitRouter
}
func (request *authenticatedRequests) requireNewServicePolicy(policyType string, serviceRoles, identityRoles []string) *servicePolicy {
policy := newServicePolicy(policyType, nil, serviceRoles, identityRoles)
request.requireCreateEntity(policy)
@@ -357,21 +363,21 @@ func (request *authenticatedRequests) requireNewIdentityWithOtt(isAdmin bool, ro
}
func (request *authenticatedRequests) requireCreateEntity(entity entity) string {
httpStatus, body := request.createEntity(entity)
request.testContext.req.Equal(http.StatusCreated, httpStatus)
id := request.testContext.getEntityId(body)
resp := request.createEntity(entity)
standardJsonResponseTests(resp, http.StatusCreated, request.testContext.testing)
id := request.testContext.getEntityId(resp.Body())
entity.setId(id)
return id
}
func (request *authenticatedRequests) requireDeleteEntity(entity entity) {
httpStatus, _ := request.deleteEntityOfType(entity.getEntityType(), entity.getId())
request.testContext.req.Equal(http.StatusOK, httpStatus)
resp := request.deleteEntityOfType(entity.getEntityType(), entity.getId())
standardJsonResponseTests(resp, http.StatusOK, request.testContext.testing)
}
func (request *authenticatedRequests) requireUpdateEntity(entity entity) {
httpStatus, _ := request.updateEntity(entity)
request.testContext.req.Equal(http.StatusOK, httpStatus)
resp := request.updateEntity(entity)
standardJsonResponseTests(resp, http.StatusOK, request.testContext.testing)
}
func (request *authenticatedRequests) requireList(url string) []string {
@@ -409,14 +415,14 @@ func (request *authenticatedRequests) requireRemoveAssociation(url string, ids .
request.testContext.req.Equal(http.StatusOK, httpStatus)
}
func (request *authenticatedRequests) createEntityOfType(entityType string, body string) (int, []byte) {
func (request *authenticatedRequests) createEntityOfType(entityType string, body string) *resty.Response {
resp, err := request.newAuthenticatedRequest().
SetBody(body).
Post("/" + entityType)
request.testContext.req.NoError(err)
request.testContext.logJson(resp.Body())
return resp.StatusCode(), resp.Body()
return resp
}
type serviceConfig struct {
@@ -490,24 +496,24 @@ func (request *authenticatedRequests) updateIdentityServiceConfigs(method string
return resp.StatusCode(), resp.Body()
}
func (request *authenticatedRequests) createEntity(entity entity) (int, []byte) {
func (request *authenticatedRequests) createEntity(entity entity) *resty.Response {
return request.createEntityOfType(entity.getEntityType(), entity.toJson(true, request.testContext))
}
func (request *authenticatedRequests) deleteEntityOfType(entityType string, id string) (int, []byte) {
func (request *authenticatedRequests) deleteEntityOfType(entityType string, id string) *resty.Response {
resp, err := request.newAuthenticatedRequest().Delete("/" + entityType + "/" + id)
request.testContext.req.NoError(err)
request.testContext.logJson(resp.Body())
return resp.StatusCode(), resp.Body()
return resp
}
func (request *authenticatedRequests) updateEntity(entity entity) (int, []byte) {
func (request *authenticatedRequests) updateEntity(entity entity) *resty.Response {
return request.updateEntityOfType(entity.getId(), entity.getEntityType(), entity.toJson(false, request.testContext), false)
}
func (request *authenticatedRequests) updateEntityOfType(id string, entityType string, body string, patch bool) (int, []byte) {
func (request *authenticatedRequests) updateEntityOfType(id string, entityType string, body string, patch bool) *resty.Response {
if request.testContext.enabledJsonLogging {
fmt.Printf("update body:\n%v\n", body)
}
@@ -528,7 +534,7 @@ func (request *authenticatedRequests) updateEntityOfType(id string, entityType s
request.testContext.req.NoError(err)
request.testContext.logJson(resp.Body())
return resp.StatusCode(), resp.Body()
return resp
}
func (request *authenticatedRequests) query(url string) (int, []byte) {
@@ -660,11 +666,11 @@ func (request *authenticatedRequests) requireCreateNewConfigType() *configType {
}
func (request *authenticatedRequests) requirePatchEntity(entity entity, fields ...string) {
httpStatus, _ := request.patchEntity(entity, fields...)
request.testContext.req.Equal(http.StatusOK, httpStatus)
resp := request.patchEntity(entity, fields...)
standardJsonResponseTests(resp, http.StatusOK, request.testContext.testing)
}
func (request *authenticatedRequests) patchEntity(entity entity, fields ...string) (int, []byte) {
func (request *authenticatedRequests) patchEntity(entity entity, fields ...string) *resty.Response {
return request.updateEntityOfType(entity.getId(), entity.getEntityType(), entity.toJson(false, request.testContext, fields...), true)
}
@@ -674,6 +680,12 @@ func (request *authenticatedRequests) getEdgeRouterJwt(edgeRouterId string) stri
return data.Data().(string)
}
func (request *authenticatedRequests) getTransitRouterJwt(transitRouterId string) string {
jsonBody := request.requireQuery("transit-routers/" + transitRouterId)
data := request.testContext.requirePath(jsonBody, "data", "enrollmentJwt")
return data.Data().(string)
}
func (request *authenticatedRequests) getIdentityJwt(identityId string) string {
request.testContext.enabledJsonLogging = true
jsonBody := request.requireQuery("identities/" + identityId)
+10 -10
View File
@@ -42,30 +42,30 @@ func Test_Configs(t *testing.T) {
configType := ctx.AdminSession.requireCreateNewConfigType()
config := ctx.newConfig(configType.id, map[string]interface{}{"port": 22})
config.name = ""
httpCode, body := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(httpCode, body, apierror.CouldNotValidateCode, "name")
resp := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.CouldNotValidateCode, "name")
})
t.Run("create without data should fail", func(t *testing.T) {
ctx.testContextChanged(t)
configType := ctx.AdminSession.requireCreateNewConfigType()
config := ctx.newConfig(configType.id, nil)
httpCode, body := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(httpCode, body, apierror.CouldNotValidateCode, "data")
resp := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.CouldNotValidateCode, "data")
})
t.Run("create without type should fail", func(t *testing.T) {
ctx.testContextChanged(t)
config := ctx.newConfig("", map[string]interface{}{"port": 22})
httpCode, body := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(httpCode, body, apierror.CouldNotValidateCode, "type")
resp := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.CouldNotValidateCode, "type")
})
t.Run("create with invalid config type should fail", func(t *testing.T) {
ctx.testContextChanged(t)
config := ctx.newConfig(uuid.New().String(), map[string]interface{}{"port": 22})
httpCode, body := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(httpCode, body, apierror.InvalidFieldCode, "type")
resp := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.InvalidFieldCode, "type")
})
t.Run("create should pass", func(t *testing.T) {
@@ -248,8 +248,8 @@ func Test_Configs(t *testing.T) {
configType.id = ctx.AdminSession.requireCreateEntity(configType)
config := ctx.newConfig(configType.id, map[string]interface{}{"port": 22})
httpCode, body := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(httpCode, body, apierror.CouldNotValidateCode, "(root)")
resp := ctx.AdminSession.createEntity(config)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.CouldNotValidateCode, "(root)")
now := time.Now()
config = ctx.newConfig(configType.id, map[string]interface{}{
+4 -4
View File
@@ -41,8 +41,8 @@ func Test_ConfigTypes(t *testing.T) {
ctx.testContextChanged(t)
configType := ctx.newConfigType()
configType.name = ""
httpCode, body := ctx.AdminSession.createEntity(configType)
ctx.requireFieldError(httpCode, body, apierror.CouldNotValidateCode, "name")
resp := ctx.AdminSession.createEntity(configType)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.CouldNotValidateCode, "name")
})
t.Run("create config type without schema should pass", func(t *testing.T) {
@@ -74,8 +74,8 @@ func Test_ConfigTypes(t *testing.T) {
},
},
}
httpCode, body := ctx.AdminSession.createEntity(configType)
ctx.requireFieldError(httpCode, body, apierror.InvalidFieldCode, "schema")
resp := ctx.AdminSession.createEntity(configType)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.InvalidFieldCode, "schema")
})
t.Run("create config type with schema should pass", func(t *testing.T) {
+34 -5
View File
@@ -60,8 +60,9 @@ import (
)
const (
ControllerConfFile = "ats-ctrl.yml"
EdgeRouterConfFile = "ats-edge.router.yml"
ControllerConfFile = "ats-ctrl.yml"
EdgeRouterConfFile = "ats-edge.router.yml"
TransitRouterConfFile = "ats-transit.router.yml"
)
func init() {
@@ -84,8 +85,10 @@ type TestContext struct {
client *resty.Client
enabledJsonLogging bool
edgeRouterEntity *edgeRouter
router *router.Router
edgeRouterEntity *edgeRouter
transitRouterEntity *transitRouter
router *router.Router
testing *testing.T
}
var defaultTestContext = &TestContext{
@@ -96,7 +99,7 @@ var defaultTestContext = &TestContext{
}
func NewTestContext(t *testing.T) *TestContext {
return &TestContext{
ret := &TestContext{
ApiHost: "127.0.0.1:1281",
AdminAuthenticator: &updbAuthenticator{
Username: uuid.New().String(),
@@ -104,6 +107,9 @@ func NewTestContext(t *testing.T) *TestContext {
},
req: require.New(t),
}
ret.testContextChanged(t)
return ret
}
func GetTestContext() *TestContext {
@@ -111,6 +117,7 @@ func GetTestContext() *TestContext {
}
func (ctx *TestContext) testContextChanged(t *testing.T) {
ctx.testing = t
ctx.req = require.New(t)
}
@@ -238,6 +245,28 @@ func (ctx *TestContext) createAndEnrollEdgeRouter(roleAttributes ...string) *edg
return ctx.edgeRouterEntity
}
func (ctx *TestContext) createAndEnrollTransitRouter() *transitRouter {
// If a tx router has already been created, delete it and create a new one
if ctx.transitRouterEntity != nil {
ctx.AdminSession.requireDeleteEntity(ctx.transitRouterEntity)
ctx.transitRouterEntity = nil
}
_ = os.MkdirAll("testdata/transit-router", os.FileMode(0755))
ctx.transitRouterEntity = ctx.AdminSession.requireNewTransitRouter()
jwt := ctx.AdminSession.getTransitRouterJwt(ctx.transitRouterEntity.id)
cfgmap, err := router.LoadConfigMap(TransitRouterConfFile)
ctx.req.NoError(err)
enroller := enroll.NewRestEnroller()
ctx.req.NoError(enroller.LoadConfig(cfgmap))
ctx.req.NoError(enroller.Enroll([]byte(jwt), true, ""))
return ctx.transitRouterEntity
}
func (ctx *TestContext) createEnrollAndStartEdgeRouter(roleAttributes ...string) {
ctx.createAndEnrollEdgeRouter(roleAttributes...)
+8
View File
@@ -90,4 +90,12 @@ func Test_EdgeRouter(t *testing.T) {
ctx.req.True(stringz.ContainsAll(list, role1, role2, role3, role4))
ctx.req.False(stringz.Contains(list, role5))
})
t.Run("newly created edge routers that is deleted", func(t *testing.T) {
ctx.testContextChanged(t)
edgeRouter := ctx.AdminSession.requireNewEdgeRouter()
ctx.AdminSession.requireDeleteEntity(edgeRouter)
})
}
+39
View File
@@ -570,6 +570,45 @@ func (entity *configValidatingService) validate(ctx *TestContext, c *gabs.Contai
}
}
func newTestTransitRouter() *transitRouter {
return &transitRouter{
name: uuid.New().String(),
}
}
type transitRouter struct {
id string
name 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")
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"))
ctx.pathEquals(c, entity.tags, path("tags"))
}
type ca struct {
id string
name string `json:"name"`
+4 -4
View File
@@ -44,8 +44,8 @@ func Test_Services(t *testing.T) {
ctx.testContextChanged(t)
service := ctx.newService(nil, nil)
service.name = ""
httpCode, body := ctx.AdminSession.createEntity(service)
ctx.requireFieldError(httpCode, body, apierror.CouldNotValidateCode, "name")
resp := ctx.AdminSession.createEntity(service)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.CouldNotValidateCode, "name")
})
t.Run("create should pass", func(t *testing.T) {
@@ -353,8 +353,8 @@ func Test_ServiceListWithConfigDuplicate(t *testing.T) {
})
service := ctx.newService(nil, s(config1.id, config2.id))
httpCode, body := ctx.AdminSession.createEntity(service)
ctx.requireFieldError(httpCode, body, apierror.InvalidFieldCode, "configs")
resp := ctx.AdminSession.createEntity(service)
ctx.requireFieldError(resp.StatusCode(), resp.Body(), apierror.InvalidFieldCode, "configs")
}
func Test_ServiceRoleAttributes(t *testing.T) {
+1
View File
@@ -1,2 +1,3 @@
ctrl.db*
edge-router/
transit-router/
+47
View File
@@ -0,0 +1,47 @@
// +build apitests
/*
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 (
"testing"
)
func Test_TransitRouters(t *testing.T) {
ctx := NewTestContext(t)
defer ctx.teardown()
ctx.startServer()
ctx.requireAdminLogin()
t.Run("transit routers can be created and enrolled", func(t *testing.T) {
ctx.testContextChanged(t)
ctx.createAndEnrollTransitRouter()
})
t.Run("create transit router, then delete", func(t *testing.T) {
ctx.testContextChanged(t)
router := ctx.AdminSession.requireNewTransitRouter()
ctx.AdminSession.requireDeleteEntity(router)
})
t.Run("create & enroll transit router, then delete", func(t *testing.T) {
ctx.testContextChanged(t)
router := ctx.createAndEnrollTransitRouter()
ctx.AdminSession.requireDeleteEntity(router)
})
}