Repository: openziti/edge (go)

Issue:
Make identity type optional. Consolidate User/Service/Device to 'Default'. 

Pull request description:
Add default identity type. Remove user,service,device types. Fixes #1428

Patch:
diff --git a/controller/internal/routes/identity_api_model.go b/controller/internal/routes/identity_api_model.go
index 62d9a037..8425dada 100644
--- a/controller/internal/routes/identity_api_model.go
+++ b/controller/internal/routes/identity_api_model.go
@@ -118,7 +118,7 @@ func MapCreateIdentityToModel(identity *rest_model.IdentityCreate, identityTypeI
 			Tags: TagsOrDefault(identity.Tags),
 		},
 		Name:                      stringz.OrEmpty(identity.Name),
-		IdentityTypeId:            identityTypeId,
+		IdentityTypeId:            persistence.DefaultIdentityType,
 		IsDefaultAdmin:            false,
 		IsAdmin:                   *identity.IsAdmin,
 		RoleAttributes:            AttributesOrDefault(identity.RoleAttributes),
diff --git a/controller/internal/routes/identity_router.go b/controller/internal/routes/identity_router.go
index 014556c5..e24ef131 100644
--- a/controller/internal/routes/identity_router.go
+++ b/controller/internal/routes/identity_router.go
@@ -192,6 +192,9 @@ func (r *IdentityRouter) Detail(ae *env.AppEnv, rc *response.RequestContext) {
 
 func getIdentityTypeId(ae *env.AppEnv, identityType rest_model.IdentityType) string {
 	//todo: Remove this, should be identityTypeId coming in through the API so we can defer this lookup and subsequent checks to the handlers
+	if identityType == rest_model.IdentityTypeDevice || identityType == rest_model.IdentityTypeService || identityType == rest_model.IdentityTypeUser {
+		return persistence.DefaultIdentityType
+	}
 	identityTypeId := ""
 	if identityType, err := ae.Managers.IdentityType.ReadByName(string(identityType)); identityType != nil && err == nil {
 		identityTypeId = identityType.Id
diff --git a/controller/model/enrollment_mod_ca.go b/controller/model/enrollment_mod_ca.go
index cf468065..de4421fc 100644
--- a/controller/model/enrollment_mod_ca.go
+++ b/controller/model/enrollment_mod_ca.go
@@ -175,17 +175,12 @@ func (module *EnrollModuleCa) completeCertAuthenticatorEnrollment(log *logrus.En
 
 	log = log.WithField("determinedName", identityName)
 
-	identType, err := module.env.GetManagers().IdentityType.ReadByName("Device")
-	if err != nil {
-		return nil, err
-	}
-
 	identity := &Identity{
 		BaseEntity: models.BaseEntity{
 			Id: identityId,
 		},
 		Name:           identityName,
-		IdentityTypeId: identType.Id,
+		IdentityTypeId: persistence.DefaultIdentityType,
 		IsDefaultAdmin: false,
 		IsAdmin:        false,
 		RoleAttributes: ca.IdentityRoles,
@@ -246,17 +241,12 @@ func (module *EnrollModuleCa) completeExternalIdEnrollment(log *logrus.Entry, co
 
 	log = log.WithField("determinedName", identityName)
 
-	identType, err := module.env.GetManagers().IdentityType.ReadByName("Device")
-	if err != nil {
-		return nil, err
-	}
-
 	identity := &Identity{
 		BaseEntity: models.BaseEntity{
 			Id: identityId,
 		},
 		Name:           identityName,
-		IdentityTypeId: identType.Id,
+		IdentityTypeId: persistence.DefaultIdentityType,
 		IsDefaultAdmin: false,
 		IsAdmin:        false,
 		RoleAttributes: ca.IdentityRoles,
@@ -264,7 +254,7 @@ func (module *EnrollModuleCa) completeExternalIdEnrollment(log *logrus.Entry, co
 
 	identity.ExternalId = &externalId
 
-	if err = module.env.GetManagers().Identity.Create(identity, context.GetChangeContext()); err != nil {
+	if err := module.env.GetManagers().Identity.Create(identity, context.GetChangeContext()); err != nil {
 		log.WithError(err).Error("failed to create identity, enrollment failed")
 		return nil, err
 	}
diff --git a/controller/model/identity_manager.go b/controller/model/identity_manager.go
index 8c09ce1a..eb5d6feb 100644
--- a/controller/model/identity_manager.go
+++ b/controller/model/identity_manager.go
@@ -208,7 +208,7 @@ func (self *IdentityManager) InitializeDefaultAdmin(username, password, name str
 		return errorz.NewFieldError(fmt.Sprintf("name must be at most %v characters", maxDefaultAdminNameLength), "name", name)
 	}
 
-	identityType, err := self.env.GetManagers().IdentityType.ReadByName(IdentityTypeUser)
+	identityType, err := self.env.GetManagers().IdentityType.ReadByName(persistence.DefaultIdentityType)
 
 	if err != nil {
 		return err
diff --git a/controller/model/identity_type_model.go b/controller/model/identity_type_model.go
index 9eb80886..c61fdb14 100644
--- a/controller/model/identity_type_model.go
+++ b/controller/model/identity_type_model.go
@@ -23,10 +23,6 @@ import (
 	"go.etcd.io/bbolt"
 )
 
-const (
-	IdentityTypeUser = "User"
-)
-
 type IdentityType struct {
 	models.BaseEntity
 	Name string `json:"name"`
diff --git a/controller/persistence/identity_type_store.go b/controller/persistence/identity_type_store.go
index 83e9b1f2..9a8d6387 100644
--- a/controller/persistence/identity_type_store.go
+++ b/controller/persistence/identity_type_store.go
@@ -21,7 +21,8 @@ import (
 )
 
 const (
-	RouterIdentityType = "Router"
+	RouterIdentityType  = "Router"
+	DefaultIdentityType = "Default"
 )
 
 type IdentityType struct {
diff --git a/controller/persistence/migration_initialize.go b/controller/persistence/migration_initialize.go
index bf51770c..be0774b1 100644
--- a/controller/persistence/migration_initialize.go
+++ b/controller/persistence/migration_initialize.go
@@ -47,9 +47,7 @@ func (m *Migrations) initialize(step *boltz.MigrationStep) int {
 }
 
 var IdentityTypesV1 = map[string]string{
-	"User":    "User",
-	"Device":  "Device",
-	"Service": "Service",
+	"Default": "Default",
 	"Router":  "Router",
 }
 
diff --git a/controller/persistence/migration_v33.go b/controller/persistence/migration_v33.go
new file mode 100644
index 00000000..5878e9b4
--- /dev/null
+++ b/controller/persistence/migration_v33.go
@@ -0,0 +1,49 @@
+/*
+	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/openziti/storage/ast"
+	"github.com/openziti/storage/boltz"
+)
+
+func (m *Migrations) migrateIdentityTypesToDefault(step *boltz.MigrationStep) {
+	step.SetError(m.stores.IdentityType.Create(step.Ctx, &IdentityType{
+		BaseExtEntity: *boltz.NewExtEntity(DefaultIdentityType, nil),
+		Name:          DefaultIdentityType,
+	}))
+
+	cursor := m.stores.Identity.IterateIds(step.Ctx.Tx(), ast.BoolNodeTrue)
+	for cursor.IsValid() {
+		id := string(cursor.Current())
+		if identityEntity, err := m.stores.Identity.LoadById(step.Ctx.Tx(), id); !step.SetError(err) {
+			if identityEntity.IdentityTypeId != RouterIdentityType {
+				identityEntity.IdentityTypeId = DefaultIdentityType
+				err = m.stores.Identity.Update(step.Ctx, identityEntity, boltz.MapFieldChecker{
+					FieldIdentityType: struct{}{},
+					"identityTypeId":  struct{}{},
+				})
+				step.SetError(err)
+			}
+		}
+		cursor.Next()
+	}
+
+	step.SetError(m.stores.IdentityType.DeleteById(step.Ctx, "User"))
+	step.SetError(m.stores.IdentityType.DeleteById(step.Ctx, "Service"))
+	step.SetError(m.stores.IdentityType.DeleteById(step.Ctx, "Device"))
+}
diff --git a/controller/persistence/migrations.go b/controller/persistence/migrations.go
index 96d6aa57..a0af1165 100644
--- a/controller/persistence/migrations.go
+++ b/controller/persistence/migrations.go
@@ -24,7 +24,7 @@ import (
 )
 
 const (
-	CurrentDbVersion = 32
+	CurrentDbVersion = 33
 	FieldVersion     = "version"
 )
 
@@ -146,6 +146,10 @@ func (m *Migrations) migrate(step *boltz.MigrationStep) int {
 		step.SetError(m.stores.ConfigType.Update(step.Ctx, interceptV1ConfigType, nil))
 	}
 
+	if step.CurrentVersion < 33 {
+		m.migrateIdentityTypesToDefault(step)
+	}
+
 	// current version
 	if step.CurrentVersion <= CurrentDbVersion {
 		return CurrentDbVersion


Test patch:
diff --git a/controller/model/testing.go b/controller/model/testing.go
index 891f54ed..ba196912 100644
--- a/controller/model/testing.go
+++ b/controller/model/testing.go
@@ -178,15 +178,13 @@ func (ctx *TestContext) Cleanup() {
 }
 
 func (ctx *TestContext) requireNewIdentity(isAdmin bool) *Identity {
-	identityType, err := ctx.managers.IdentityType.ReadByIdOrName("Service")
-	ctx.NoError(err)
-	identity := &Identity{
+	newIdentity := &Identity{
 		Name:           eid.New(),
 		IsAdmin:        isAdmin,
-		IdentityTypeId: identityType.Id,
+		IdentityTypeId: persistence.DefaultIdentityType,
 	}
-	ctx.NoError(ctx.managers.Identity.Create(identity, change.New()))
-	return identity
+	ctx.NoError(ctx.managers.Identity.Create(newIdentity, change.New()))
+	return newIdentity
 }
 
 func (ctx *TestContext) requireNewService() *Service {
diff --git a/tests/authenticate.go b/tests/authenticate.go
index 1deba087..c4a9bf4e 100644
--- a/tests/authenticate.go
+++ b/tests/authenticate.go
@@ -377,7 +377,7 @@ func (request *authenticatedRequests) RequireCreateSdkContext(roleAttributes ...
 func (request *authenticatedRequests) requireCreateIdentity(name string, isAdmin bool, rolesAttributes ...string) string {
 	entityData := gabs.New()
 	request.testContext.setJsonValue(entityData, name, "name")
-	request.testContext.setJsonValue(entityData, "User", "type")
+	request.testContext.setJsonValue(entityData, rest_model.IdentityTypeDefault, "type")
 	request.testContext.setJsonValue(entityData, isAdmin, "isAdmin")
 	request.testContext.setJsonValue(entityData, rolesAttributes, "roleAttributes")
 
@@ -471,7 +471,7 @@ func (request *authenticatedRequests) requireCreateIdentityWithUpdbEnrollment(na
 
 	identity := &identity{
 		name:         name,
-		identityType: "User",
+		identityType: string(rest_model.IdentityTypeDefault),
 		isAdmin:      isAdmin,
 		enrollment: map[string]interface{}{
 			"updb": name,
@@ -487,7 +487,7 @@ func (request *authenticatedRequests) requireCreateIdentityWithUpdbEnrollment(na
 func (request *authenticatedRequests) requireCreateIdentityOttEnrollment(name string, isAdmin bool, rolesAttributes ...string) (string, *certAuthenticator) {
 	entityData := gabs.New()
 	request.testContext.setJsonValue(entityData, name, "name")
-	request.testContext.setJsonValue(entityData, "User", "type")
+	request.testContext.setJsonValue(entityData, rest_model.IdentityTypeDefault, "type")
 	request.testContext.setJsonValue(entityData, isAdmin, "isAdmin")
 	request.testContext.setJsonValue(entityData, rolesAttributes, "roleAttributes")
 
@@ -506,7 +506,7 @@ func (request *authenticatedRequests) requireCreateIdentityOttEnrollment(name st
 func (request *authenticatedRequests) requireCreateIdentityOttEnrollmentUnfinished(name string, isAdmin bool, rolesAttributes ...string) string {
 	entityData := gabs.New()
 	request.testContext.setJsonValue(entityData, name, "name")
-	request.testContext.setJsonValue(entityData, "User", "type")
+	request.testContext.setJsonValue(entityData, rest_model.IdentityTypeDefault, "type")
 	request.testContext.setJsonValue(entityData, isAdmin, "isAdmin")
 	request.testContext.setJsonValue(entityData, rolesAttributes, "roleAttributes")
 
diff --git a/tests/ca_test.go b/tests/ca_test.go
index ad386889..e6f2f8be 100644
--- a/tests/ca_test.go
+++ b/tests/ca_test.go
@@ -1,5 +1,4 @@
 //go:build apitests
-// +build apitests
 
 package tests
 
diff --git a/tests/enrollment_updb_test.go b/tests/enrollment_updb_test.go
index f06aaa2c..15103ee7 100644
--- a/tests/enrollment_updb_test.go
+++ b/tests/enrollment_updb_test.go
@@ -1,5 +1,4 @@
 //go:build apitests
-// +build apitests
 
 /*
 	Copyright NetFoundry Inc.
@@ -22,6 +21,7 @@ package tests
 import (
 	"github.com/Jeffail/gabs"
 	"github.com/google/uuid"
+	"github.com/openziti/edge-api/rest_model"
 	"net/http"
 	"testing"
 )
@@ -40,7 +40,7 @@ func Test_EnrollmentUpdb(t *testing.T) {
 		updbName := uuid.New().String()
 		updbUsername := uuid.New().String()
 		updbPassword := uuid.New().String()
-		updbType := "User"
+		updbType := rest_model.IdentityTypeDefault
 
 		_, err := updbCreate.Set(updbName, "name")
 		ctx.Req.NoError(err)
diff --git a/tests/entities.go b/tests/entities.go
index 3052d34d..7b2e1d86 100644
--- a/tests/entities.go
+++ b/tests/entities.go
@@ -25,6 +25,7 @@ import (
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"encoding/pem"
+	"github.com/openziti/edge-api/rest_model"
 	"github.com/openziti/edge/common/eid"
 	"github.com/openziti/sdk-golang/ziti"
 	"math/big"
@@ -243,7 +244,7 @@ func (entity *terminator) fromJson(ctx *TestContext, c *gabs.Container) {
 func newTestIdentity(isAdmin bool, roleAttributes ...string) *identity {
 	return &identity{
 		name:           eid.New(),
-		identityType:   "User",
+		identityType:   string(rest_model.IdentityTypeDefault),
 		isAdmin:        isAdmin,
 		roleAttributes: roleAttributes,
 	}
diff --git a/tests/identity_test.go b/tests/identity_test.go
index 2518fa45..79cbefe6 100644
--- a/tests/identity_test.go
+++ b/tests/identity_test.go
@@ -328,7 +328,7 @@ func Test_Identity(t *testing.T) {
 		ctx.testContextChanged(t)
 
 		terminatorCost := rest_model.TerminatorCost(1)
-		identityType := rest_model.IdentityTypeDevice
+		identityType := rest_model.IdentityTypeDefault
 
 		identityCreate := &rest_model.IdentityCreate{
 			AppData: &rest_model.Tags{
@@ -401,7 +401,7 @@ func Test_Identity(t *testing.T) {
 			ctx.testContextChanged(t)
 			updateContent := gabs.New()
 			_, _ = updateContent.SetP(eid.New(), "name")
-			_, _ = updateContent.SetP("Device", "type")
+			_, _ = updateContent.SetP(rest_model.IdentityTypeDefault, "type")
 			_, _ = updateContent.SetP(map[string]interface{}{}, "tags")
 			_, _ = updateContent.SetP(false, "isAdmin")
 			_, _ = updateContent.SetP("", "authPolicyId")
@@ -424,7 +424,8 @@ func Test_Identity(t *testing.T) {
 			ctx.testContextChanged(t)
 			updateContent := gabs.New()
 			_, _ = updateContent.SetP(eid.New(), "name")
-			_, _ = updateContent.SetP("Device", "type")
+			_, _ = updateContent.SetP(rest_model.IdentityTypeDefault, "type")
+			_, _ = updateContent.SetP(rest_model.IdentityTypeDefault, "type")
 			_, _ = updateContent.SetP(map[string]interface{}{}, "tags")
 			_, _ = updateContent.SetP(false, "isAdmin")
 			_, _ = updateContent.SetP("", "authPolicyId")
@@ -449,7 +450,7 @@ func Test_Identity(t *testing.T) {
 
 			updateContent := gabs.New()
 			_, _ = updateContent.SetP(eid.New(), "name")
-			_, _ = updateContent.SetP("Device", "type")
+			_, _ = updateContent.SetP(rest_model.IdentityTypeDefault, "type")
 			_, _ = updateContent.SetP(map[string]interface{}{}, "tags")
 			_, _ = updateContent.SetP(true, "isAdmin")
 			_, _ = updateContent.SetP(true, "isDefaultAdmin")
@@ -472,7 +473,7 @@ func Test_Identity(t *testing.T) {
 			newName := eid.New()
 			updateContent := gabs.New()
 			_, _ = updateContent.SetP(newName, "name")
-			_, _ = updateContent.SetP("Device", "type")
+			_, _ = updateContent.SetP(rest_model.IdentityTypeDefault, "type")
 			_, _ = updateContent.SetP(map[string]interface{}{}, "tags")
 			_, _ = updateContent.SetP(false, "isAdmin")
 			_, _ = updateContent.SetP("", "authPolicyId")
@@ -562,7 +563,7 @@ func Test_Identity(t *testing.T) {
 	t.Run("disable and enable identities affect cert authentication", func(t *testing.T) {
 		ctx.testContextChanged(t)
 
-		identityType := rest_model.IdentityTypeDevice
+		identityType := rest_model.IdentityTypeDefault
 		identity := &rest_model.IdentityCreate{
 			IsAdmin: B(false),
 			Name:    S("test-identity-disable-cert"),
@@ -635,7 +636,7 @@ func Test_Identity(t *testing.T) {
 		ctx.testContextChanged(t)
 		username := "test-identity-disable-updb"
 		password := "test-identity-disable-updb-password"
-		identityType := rest_model.IdentityTypeDevice
+		identityType := rest_model.IdentityTypeDefault
 		identity := &rest_model.IdentityCreate{
 			IsAdmin: B(false),
 			Name:    S("test-identity-disable-updb"),
@@ -741,7 +742,7 @@ func Test_Identity(t *testing.T) {
 		ctx.NoError(err)
 		ctx.Equal(http.StatusOK, resp.StatusCode())
 
-		identityType := rest_model.IdentityTypeDevice
+		identityType := rest_model.IdentityTypeDefault
 		identity := &rest_model.IdentityCreate{
 			IsAdmin: B(false),
 			Name:    S("test-identity-disable-updb-01"),
diff --git a/tests/model_sdk_performance_test.go b/tests/model_sdk_performance_test.go
index 946f14fd..d37a310e 100644
--- a/tests/model_sdk_performance_test.go
+++ b/tests/model_sdk_performance_test.go
@@ -1,5 +1,4 @@
 //go:build perftests
-// +build perftests
 
 package tests
 
@@ -266,7 +265,7 @@ func (ctx *modelPerf) createIdentities(spec *perfScenarioSpec) {
 				Id: id,
 			},
 			Name:           id,
-			IdentityTypeId: "User",
+			IdentityTypeId: string(rest_model.IdentityTypeDefault),
 			IsDefaultAdmin: false,
 			IsAdmin:        false,
 			RoleAttributes: spec.identityAttrs[i],