From 57dce28c3e9a5820835be89b46a367a1c2d1956a Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:26:10 -0300 Subject: [PATCH 01/30] is: Add migration for removing contact info rows --- ...20240100200000_contact_info_removal.up.sql | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 pkg/identityserver/store/migrations/20240100200000_contact_info_removal.up.sql diff --git a/pkg/identityserver/store/migrations/20240100200000_contact_info_removal.up.sql b/pkg/identityserver/store/migrations/20240100200000_contact_info_removal.up.sql new file mode 100644 index 0000000000..245e08c6ee --- /dev/null +++ b/pkg/identityserver/store/migrations/20240100200000_contact_info_removal.up.sql @@ -0,0 +1,23 @@ +delete from contact_infos +where entity_type = 'application' +; +--bun:split +delete from contact_infos +where entity_type = 'client' +; +--bun:split +delete from contact_infos +where entity_type = 'end_device' +; +--bun:split +delete from contact_infos +where entity_type = 'gateway' +; +--bun:split +delete from contact_infos +where entity_type = 'organization' +; +--bun:split +delete from contact_infos +where entity_type = 'user' +; From a668b601eb41ae41b98d07dbe3cbe57cbd766784 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:27:47 -0300 Subject: [PATCH 02/30] is: Remove ContactInfo references from gateway store --- pkg/identityserver/bunstore/gateway_store.go | 39 +------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/pkg/identityserver/bunstore/gateway_store.go b/pkg/identityserver/bunstore/gateway_store.go index 78cd8f25bf..3ee3364e2f 100644 --- a/pkg/identityserver/bunstore/gateway_store.go +++ b/pkg/identityserver/bunstore/gateway_store.go @@ -17,7 +17,6 @@ package store import ( "context" "fmt" - "sort" "strings" "time" @@ -49,8 +48,6 @@ type Gateway struct { Attributes []*Attribute `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - ContactInfo []*ContactInfo `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - AdministrativeContactID *string `bun:"administrative_contact_id,type:uuid"` AdministrativeContact *Account `bun:"rel:belongs-to,join:administrative_contact_id=id"` @@ -185,14 +182,6 @@ func gatewayToPB(m *Gateway, fieldMask ...string) (*ttnpb.Gateway, error) { } } - if len(m.ContactInfo) > 0 { - pb.ContactInfo = make([]*ttnpb.ContactInfo, len(m.ContactInfo)) - for i, contactInfo := range m.ContactInfo { - pb.ContactInfo[i] = contactInfoToPB(contactInfo) - } - sort.Sort(contactInfoProtoSlice(pb.ContactInfo)) - } - if m.AdministrativeContact != nil { pb.AdministrativeContact = m.AdministrativeContact.GetOrganizationOrUserIdentifiers() } @@ -321,15 +310,6 @@ func (s *gatewayStore) CreateGateway( } } - if len(pb.ContactInfo) > 0 { - gatewayModel.ContactInfo, err = s.replaceContactInfo( - ctx, nil, pb.ContactInfo, "gateway", gatewayModel.ID, - ) - if err != nil { - return nil, err - } - } - if len(pb.Antennas) > 0 { gatewayModel.Antennas, err = s.replaceGatewayAntennas( ctx, nil, pb.Antennas, gatewayModel.ID, @@ -397,8 +377,6 @@ func (*gatewayStore) selectWithFields(q *bun.SelectQuery, fieldMask store.FieldM columns = append(columns, "supports_lrfhss") case "attributes": q = q.Relation("Attributes") - case "contact_info": - q = q.Relation("ContactInfo") case "administrative_contact": q = q.Relation("AdministrativeContact", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column("uid", "account_type") @@ -608,14 +586,6 @@ func (s *gatewayStore) updateGatewayModel( //nolint:gocyclo return err } - case "contact_info": - model.ContactInfo, err = s.replaceContactInfo( - ctx, model.ContactInfo, pb.ContactInfo, "gateway", model.ID, - ) - if err != nil { - return err - } - case "administrative_contact": if contact := pb.AdministrativeContact; contact != nil { account, err := s.getAccountModel(ctx, contact.EntityType(), contact.IDString()) @@ -921,7 +891,7 @@ func (s *gatewayStore) PurgeGateway(ctx context.Context, id *ttnpb.GatewayIdenti model, err := s.getGatewayModelBy( store.WithSoftDeleted(ctx, false), s.selectWithID(ctx, id.GetGatewayId()), - store.FieldMask{"attributes", "contact_info", "antennas"}, + store.FieldMask{"attributes", "antennas"}, ) if err != nil { if errors.IsNotFound(err) { @@ -939,13 +909,6 @@ func (s *gatewayStore) PurgeGateway(ctx context.Context, id *ttnpb.GatewayIdenti } } - if len(model.ContactInfo) > 0 { - _, err = s.replaceContactInfo(ctx, model.ContactInfo, nil, "gateway", model.ID) - if err != nil { - return err - } - } - if _, err = s.replaceGatewayAntennas(ctx, model.Antennas, nil, model.ID); err != nil { return err } From 95cfc017c56604cde987c45380deddfaa85b84e4 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:29:27 -0300 Subject: [PATCH 03/30] is: Remove ContactInfo references from application store --- .../bunstore/application_store.go | 39 +------------------ 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/pkg/identityserver/bunstore/application_store.go b/pkg/identityserver/bunstore/application_store.go index 028535968a..dc4752b145 100644 --- a/pkg/identityserver/bunstore/application_store.go +++ b/pkg/identityserver/bunstore/application_store.go @@ -17,7 +17,6 @@ package store import ( "context" "fmt" - "sort" "github.com/uptrace/bun" "go.opentelemetry.io/otel/attribute" @@ -44,8 +43,6 @@ type Application struct { Attributes []*Attribute `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - ContactInfo []*ContactInfo `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - AdministrativeContactID *string `bun:"administrative_contact_id,type:uuid"` AdministrativeContact *Account `bun:"rel:belongs-to,join:administrative_contact_id=id"` @@ -94,14 +91,6 @@ func applicationToPB(m *Application, fieldMask ...string) (*ttnpb.Application, e } } - if len(m.ContactInfo) > 0 { - pb.ContactInfo = make([]*ttnpb.ContactInfo, len(m.ContactInfo)) - for i, contactInfo := range m.ContactInfo { - pb.ContactInfo[i] = contactInfoToPB(contactInfo) - } - sort.Sort(contactInfoProtoSlice(pb.ContactInfo)) - } - if m.AdministrativeContact != nil { pb.AdministrativeContact = m.AdministrativeContact.GetOrganizationOrUserIdentifiers() } @@ -190,15 +179,6 @@ func (s *applicationStore) CreateApplication( } } - if len(pb.ContactInfo) > 0 { - applicationModel.ContactInfo, err = s.replaceContactInfo( - ctx, nil, pb.ContactInfo, "application", applicationModel.ID, - ) - if err != nil { - return nil, err - } - } - pb, err = applicationToPB(applicationModel) if err != nil { return nil, err @@ -231,8 +211,6 @@ func (*applicationStore) selectWithFields(q *bun.SelectQuery, fieldMask store.Fi columns = append(columns, f) case "attributes": q = q.Relation("Attributes") - case "contact_info": - q = q.Relation("ContactInfo") case "administrative_contact": q = q.Relation("AdministrativeContact", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column("uid", "account_type") @@ -403,14 +381,6 @@ func (s *applicationStore) updateApplicationModel( //nolint:gocyclo return err } - case "contact_info": - model.ContactInfo, err = s.replaceContactInfo( - ctx, model.ContactInfo, pb.ContactInfo, "application", model.ID, - ) - if err != nil { - return err - } - case "administrative_contact": if contact := pb.AdministrativeContact; contact != nil { account, err := s.getAccountModel(ctx, contact.EntityType(), contact.IDString()) @@ -570,7 +540,7 @@ func (s *applicationStore) PurgeApplication(ctx context.Context, id *ttnpb.Appli model, err := s.getApplicationModelBy( store.WithSoftDeleted(ctx, false), s.selectWithID(ctx, id.GetApplicationId()), - store.FieldMask{"attributes", "contact_info"}, + store.FieldMask{"attributes"}, ) if err != nil { if errors.IsNotFound(err) { @@ -588,13 +558,6 @@ func (s *applicationStore) PurgeApplication(ctx context.Context, id *ttnpb.Appli } } - if len(model.ContactInfo) > 0 { - _, err = s.replaceContactInfo(ctx, model.ContactInfo, nil, "application", model.ID) - if err != nil { - return err - } - } - _, err = s.DB.NewDelete(). Model(model). WherePK(). From acc0de4bd82afd8843415b833235fa4654f12413 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:31:04 -0300 Subject: [PATCH 04/30] is: Remove ContactInfo references from client store --- pkg/identityserver/bunstore/client_store.go | 39 +-------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/pkg/identityserver/bunstore/client_store.go b/pkg/identityserver/bunstore/client_store.go index 95755fe829..dec3fd5485 100644 --- a/pkg/identityserver/bunstore/client_store.go +++ b/pkg/identityserver/bunstore/client_store.go @@ -17,7 +17,6 @@ package store import ( "context" "fmt" - "sort" "github.com/uptrace/bun" "go.opentelemetry.io/otel/attribute" @@ -44,8 +43,6 @@ type Client struct { Attributes []*Attribute `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - ContactInfo []*ContactInfo `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - AdministrativeContactID *string `bun:"administrative_contact_id,type:uuid"` AdministrativeContact *Account `bun:"rel:belongs-to,join:administrative_contact_id=id"` @@ -110,14 +107,6 @@ func clientToPB(m *Client, fieldMask ...string) (*ttnpb.Client, error) { } } - if len(m.ContactInfo) > 0 { - pb.ContactInfo = make([]*ttnpb.ContactInfo, len(m.ContactInfo)) - for i, contactInfo := range m.ContactInfo { - pb.ContactInfo[i] = contactInfoToPB(contactInfo) - } - sort.Sort(contactInfoProtoSlice(pb.ContactInfo)) - } - if m.AdministrativeContact != nil { pb.AdministrativeContact = m.AdministrativeContact.GetOrganizationOrUserIdentifiers() } @@ -214,15 +203,6 @@ func (s *clientStore) CreateClient( } } - if len(pb.ContactInfo) > 0 { - clientModel.ContactInfo, err = s.replaceContactInfo( - ctx, nil, pb.ContactInfo, "client", clientModel.ID, - ) - if err != nil { - return nil, err - } - } - pb, err = clientToPB(clientModel) if err != nil { return nil, err @@ -259,8 +239,6 @@ func (*clientStore) selectWithFields(q *bun.SelectQuery, fieldMask store.FieldMa columns = append(columns, "client_secret") case "attributes": q = q.Relation("Attributes") - case "contact_info": - q = q.Relation("ContactInfo") case "administrative_contact": q = q.Relation("AdministrativeContact", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column("uid", "account_type") @@ -431,14 +409,6 @@ func (s *clientStore) updateClientModel( //nolint:gocyclo return err } - case "contact_info": - model.ContactInfo, err = s.replaceContactInfo( - ctx, model.ContactInfo, pb.ContactInfo, "client", model.ID, - ) - if err != nil { - return err - } - case "administrative_contact": if contact := pb.AdministrativeContact; contact != nil { account, err := s.getAccountModel(ctx, contact.EntityType(), contact.IDString()) @@ -615,7 +585,7 @@ func (s *clientStore) PurgeClient(ctx context.Context, id *ttnpb.ClientIdentifie model, err := s.getClientModelBy( store.WithSoftDeleted(ctx, false), s.selectWithID(ctx, id.GetClientId()), - store.FieldMask{"attributes", "contact_info"}, + store.FieldMask{"attributes"}, ) if err != nil { if errors.IsNotFound(err) { @@ -633,13 +603,6 @@ func (s *clientStore) PurgeClient(ctx context.Context, id *ttnpb.ClientIdentifie } } - if len(model.ContactInfo) > 0 { - _, err = s.replaceContactInfo(ctx, model.ContactInfo, nil, "client", model.ID) - if err != nil { - return err - } - } - _, err = s.DB.NewDelete(). Model(model). WherePK(). From c71c2db9ee828fa65b92be56e8f401c3a65c90f9 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:31:44 -0300 Subject: [PATCH 05/30] is: Remove ContactInfo references from end-device store --- pkg/identityserver/bunstore/end_device_store.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/identityserver/bunstore/end_device_store.go b/pkg/identityserver/bunstore/end_device_store.go index 05aa9a0a33..c07437e0f7 100644 --- a/pkg/identityserver/bunstore/end_device_store.go +++ b/pkg/identityserver/bunstore/end_device_store.go @@ -328,8 +328,6 @@ func (*endDeviceStore) selectWithFields(q *bun.SelectQuery, fieldMask store.Fiel columns = append(columns, "brand_id", "model_id", "hardware_version", "firmware_version", "band_id") case "attributes": q = q.Relation("Attributes") - case "contact_info": - q = q.Relation("ContactInfo") case "locations": q = q.Relation("Locations") case "picture": From 3a8a180864163d3282687a8c75216ef014f2d693 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:32:48 -0300 Subject: [PATCH 06/30] is: Remove ContactInfo references from organization store --- .../bunstore/organization_store.go | 39 +------------------ 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/pkg/identityserver/bunstore/organization_store.go b/pkg/identityserver/bunstore/organization_store.go index 03c88ae497..6cac8270ae 100644 --- a/pkg/identityserver/bunstore/organization_store.go +++ b/pkg/identityserver/bunstore/organization_store.go @@ -17,7 +17,6 @@ package store import ( "context" "fmt" - "sort" "github.com/uptrace/bun" "go.opentelemetry.io/otel/attribute" @@ -44,8 +43,6 @@ type Organization struct { Attributes []*Attribute `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - ContactInfo []*ContactInfo `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - AdministrativeContactID *string `bun:"administrative_contact_id,type:uuid"` AdministrativeContact *Account `bun:"rel:belongs-to,join:administrative_contact_id=id"` @@ -85,14 +82,6 @@ func organizationToPB(m *Organization, fieldMask ...string) (*ttnpb.Organization } } - if len(m.ContactInfo) > 0 { - pb.ContactInfo = make([]*ttnpb.ContactInfo, len(m.ContactInfo)) - for i, contactInfo := range m.ContactInfo { - pb.ContactInfo[i] = contactInfoToPB(contactInfo) - } - sort.Sort(contactInfoProtoSlice(pb.ContactInfo)) - } - if m.AdministrativeContact != nil { pb.AdministrativeContact = m.AdministrativeContact.GetOrganizationOrUserIdentifiers() } @@ -198,15 +187,6 @@ func (s *organizationStore) CreateOrganization( } } - if len(pb.ContactInfo) > 0 { - organizationModel.ContactInfo, err = s.replaceContactInfo( - ctx, nil, pb.ContactInfo, "organization", organizationModel.ID, - ) - if err != nil { - return nil, err - } - } - pb, err = organizationToPB(organizationModel) if err != nil { return nil, err @@ -238,8 +218,6 @@ func (*organizationStore) selectWithFields(q *bun.SelectQuery, fieldMask store.F columns = append(columns, f) case "attributes": q = q.Relation("Attributes") - case "contact_info": - q = q.Relation("ContactInfo") case "administrative_contact": q = q.Relation("AdministrativeContact", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column("uid", "account_type") @@ -410,14 +388,6 @@ func (s *organizationStore) updateOrganizationModel( //nolint:gocyclo return err } - case "contact_info": - model.ContactInfo, err = s.replaceContactInfo( - ctx, model.ContactInfo, pb.ContactInfo, "organization", model.ID, - ) - if err != nil { - return err - } - case "administrative_contact": if contact := pb.AdministrativeContact; contact != nil { account, err := s.getAccountModel(ctx, contact.EntityType(), contact.IDString()) @@ -606,7 +576,7 @@ func (s *organizationStore) PurgeOrganization(ctx context.Context, id *ttnpb.Org model, err := s.getOrganizationModelBy( store.WithSoftDeleted(ctx, false), s.selectWithID(ctx, id.GetOrganizationId()), - store.FieldMask{"attributes", "contact_info"}, + store.FieldMask{"attributes"}, ) if err != nil { if errors.IsNotFound(err) { @@ -624,13 +594,6 @@ func (s *organizationStore) PurgeOrganization(ctx context.Context, id *ttnpb.Org } } - if len(model.ContactInfo) > 0 { - _, err = s.replaceContactInfo(ctx, model.ContactInfo, nil, "organization", model.ID) - if err != nil { - return err - } - } - _, err = s.DB.NewDelete(). Model(model). WherePK(). From eb09e5af8712cc03674dcf97e050c0d423f833e7 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:33:38 -0300 Subject: [PATCH 07/30] is: Remove ContactInfo references from user store --- pkg/identityserver/bunstore/user_store.go | 39 +---------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/pkg/identityserver/bunstore/user_store.go b/pkg/identityserver/bunstore/user_store.go index 8ed5ebcbb1..cbe10fcd4d 100644 --- a/pkg/identityserver/bunstore/user_store.go +++ b/pkg/identityserver/bunstore/user_store.go @@ -17,7 +17,6 @@ package store import ( "context" "fmt" - "sort" "time" "github.com/uptrace/bun" @@ -45,8 +44,6 @@ type User struct { Attributes []*Attribute `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - ContactInfo []*ContactInfo `bun:"rel:has-many,join:type=entity_type,join:id=entity_id,polymorphic"` - PrimaryEmailAddress string `bun:"primary_email_address,notnull"` PrimaryEmailAddressValidatedAt *time.Time `bun:"primary_email_address_validated_at"` @@ -112,14 +109,6 @@ func userToPB(m *User, fieldMask ...string) (*ttnpb.User, error) { } } - if len(m.ContactInfo) > 0 { - pb.ContactInfo = make([]*ttnpb.ContactInfo, len(m.ContactInfo)) - for i, contactInfo := range m.ContactInfo { - pb.ContactInfo[i] = contactInfoToPB(contactInfo) - } - sort.Sort(contactInfoProtoSlice(pb.ContactInfo)) - } - if m.ProfilePicture != nil { picture, err := pictureToPB(m.ProfilePicture) if err != nil { @@ -234,15 +223,6 @@ func (s *userStore) CreateUser(ctx context.Context, pb *ttnpb.User) (*ttnpb.User } } - if len(pb.ContactInfo) > 0 { - userModel.ContactInfo, err = s.replaceContactInfo( - ctx, nil, pb.ContactInfo, "user", userModel.ID, - ) - if err != nil { - return nil, err - } - } - pb, err = userToPB(userModel) if err != nil { return nil, err @@ -279,8 +259,6 @@ func (*userStore) selectWithFields(q *bun.SelectQuery, fieldMask store.FieldMask columns = append(columns, f) case "attributes": q = q.Relation("Attributes") - case "contact_info": - q = q.Relation("ContactInfo") case "administrative_contact": q = q.Relation("AdministrativeContact", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column("uid", "account_type") @@ -497,14 +475,6 @@ func (s *userStore) updateUserModel( //nolint:gocyclo return err } - case "contact_info": - model.ContactInfo, err = s.replaceContactInfo( - ctx, model.ContactInfo, pb.ContactInfo, "user", model.ID, - ) - if err != nil { - return err - } - case "primary_email_address": model.PrimaryEmailAddress = pb.PrimaryEmailAddress columns = append(columns, "primary_email_address") @@ -756,7 +726,7 @@ func (s *userStore) PurgeUser(ctx context.Context, id *ttnpb.UserIdentifiers) er model, err := s.getUserModelBy( store.WithSoftDeleted(ctx, false), s.selectWithID(ctx, id.GetUserId()), - store.FieldMask{"attributes", "contact_info"}, + store.FieldMask{"attributes"}, ) if err != nil { if errors.IsNotFound(err) { @@ -774,13 +744,6 @@ func (s *userStore) PurgeUser(ctx context.Context, id *ttnpb.UserIdentifiers) er } } - if len(model.ContactInfo) > 0 { - _, err = s.replaceContactInfo(ctx, model.ContactInfo, nil, "user", model.ID) - if err != nil { - return err - } - } - _, err = s.DB.NewDelete(). Model(model). WherePK(). From 3ceea50d163f99ef48271db694a68ada929d56b3 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 16:34:19 -0300 Subject: [PATCH 08/30] is: Remove ContactInfo store test from bunstore --- pkg/identityserver/bunstore/store_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pkg/identityserver/bunstore/store_test.go b/pkg/identityserver/bunstore/store_test.go index a9e22e08e2..a3c96e49f6 100644 --- a/pkg/identityserver/bunstore/store_test.go +++ b/pkg/identityserver/bunstore/store_test.go @@ -142,13 +142,6 @@ func TestMembershipStore(t *testing.T) { st.TestMembershipStorePagination(t) } -func TestContactInfoStore(t *testing.T) { - t.Parallel() - - st := storetest.New(t, newTestStore) - st.TestContactInfoStoreCRUD(t) -} - func TestEmailValidationStore(t *testing.T) { t.Parallel() From 3e62192749d27ab983994be206ad8a67299639c0 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 17:39:50 -0300 Subject: [PATCH 09/30] is: Remove ContactInfo from user registry write operations --- pkg/identityserver/user_registry.go | 120 ++++------------------------ 1 file changed, 16 insertions(+), 104 deletions(-) diff --git a/pkg/identityserver/user_registry.go b/pkg/identityserver/user_registry.go index 6ff9c03179..850ab58b0b 100644 --- a/pkg/identityserver/user_registry.go +++ b/pkg/identityserver/user_registry.go @@ -30,6 +30,7 @@ import ( "go.thethings.network/lorawan-stack/v3/pkg/identityserver/blocklist" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" "go.thethings.network/lorawan-stack/v3/pkg/log" + "go.thethings.network/lorawan-stack/v3/pkg/rpcmiddleware/warning" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/unique" "go.thethings.network/lorawan-stack/v3/pkg/validate" @@ -188,9 +189,6 @@ func (is *IdentityServer) createUser(ctx context.Context, req *ttnpb.CreateUserR if err := validate.Email(req.User.PrimaryEmailAddress); err != nil { return nil, err } - if err := validateContactInfo(req.User.ContactInfo); err != nil { - return nil, err - } if !createdByAdmin { if !config.UserRegistration.Enabled { @@ -208,26 +206,6 @@ func (is *IdentityServer) createUser(ctx context.Context, req *ttnpb.CreateUserR req.User.TemporaryPassword = "" req.User.TemporaryPasswordCreatedAt = nil req.User.TemporaryPasswordExpiresAt = nil - cleanContactInfo(req.User.ContactInfo) - } - - var primaryEmailAddressFound bool - for _, contactInfo := range req.User.ContactInfo { - if contactInfo.ContactMethod == ttnpb.ContactMethod_CONTACT_METHOD_EMAIL && - contactInfo.Value == req.User.PrimaryEmailAddress { - primaryEmailAddressFound = true - if contactInfo.ValidatedAt != nil { - req.User.PrimaryEmailAddressValidatedAt = contactInfo.ValidatedAt - break - } - } - } - if !primaryEmailAddressFound { - req.User.ContactInfo = append(req.User.ContactInfo, &ttnpb.ContactInfo{ - ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, - Value: req.User.PrimaryEmailAddress, - ValidatedAt: req.User.PrimaryEmailAddressValidatedAt, - }) } if err := is.validatePasswordStrength(ctx, req.User.GetIds().GetUserId(), req.User.Password); err != nil { @@ -266,13 +244,6 @@ func (is *IdentityServer) createUser(ctx context.Context, req *ttnpb.CreateUserR return err } - if len(req.User.ContactInfo) > 0 { - usr.ContactInfo, err = st.SetContactInfo(ctx, usr.GetIds(), req.User.ContactInfo) - if err != nil { - return err - } - } - if invitation != nil { if err = st.SetInvitationAcceptedBy(ctx, invitation.Token, usr.GetIds()); err != nil { return err @@ -416,9 +387,6 @@ func (is *IdentityServer) updateUser(ctx context.Context, req *ttnpb.UpdateUserR return nil, err } } - if err := validateContactInfo(req.User.ContactInfo); err != nil { - return nil, err - } if err = is.RequireAdminForFieldUpdate(ctx, req.GetFieldMask().GetPaths(), []string{ "primary_email_address_validated_at", @@ -430,9 +398,10 @@ func (is *IdentityServer) updateUser(ctx context.Context, req *ttnpb.UpdateUserR } if !updatedByAdmin { - cleanContactInfo(req.User.ContactInfo) - req.User.PrimaryEmailAddressValidatedAt = nil - req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.GetPaths(), "primary_email_address_validated_at") + if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "primary_email_address") { + req.User.PrimaryEmailAddressValidatedAt = nil + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.GetPaths(), "primary_email_address_validated_at") + } } if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "state") { @@ -442,6 +411,11 @@ func (is *IdentityServer) updateUser(ctx context.Context, req *ttnpb.UpdateUserR } } + if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { + warning.Add(ctx, "Contact info is deprecated and will be removed in the next major release") + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + } + if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "temporary_password") { hashedTemporaryPassword, err := auth.Hash(ctx, req.User.TemporaryPassword) if err != nil { @@ -472,7 +446,6 @@ func (is *IdentityServer) updateUser(ctx context.Context, req *ttnpb.UpdateUserR } updatePrimaryEmailAddress := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "primary_email_address") - updateContactInfo := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "admin") { @@ -482,78 +455,21 @@ func (is *IdentityServer) updateUser(ctx context.Context, req *ttnpb.UpdateUserR } } - var contactInfo []*ttnpb.ContactInfo - - if updateContactInfo { - contactInfo, err = st.SetContactInfo(ctx, req.User.GetIds(), req.User.ContactInfo) - if err != nil { - return err - } - } - - if updatePrimaryEmailAddress { - // If no update was done to contactInfo list then fetch it. - if !updateContactInfo { - contactInfo, err = st.GetContactInfo(ctx, req.User.GetIds()) - if err != nil { - return err - } - } - oldUser, err := st.GetUser(ctx, req.User.GetIds(), []string{"primary_email_address"}) - if err != nil { - return err - } - oldEmailAddress := oldUser.PrimaryEmailAddress - - foundOldEmailAddress := false - for _, contactInfo := range contactInfo { - if contactInfo.ContactMethod != ttnpb.ContactMethod_CONTACT_METHOD_EMAIL { - continue - } - if contactInfo.Value == oldEmailAddress { - foundOldEmailAddress = true - - if updatedByAdmin { - req.User.PrimaryEmailAddressValidatedAt = contactInfo.ValidatedAt - req.FieldMask.Paths = ttnpb.AddFields( - req.FieldMask.GetPaths(), "primary_email_address_validated_at", - ) - } - - contactInfo.Value = req.User.PrimaryEmailAddress - contactInfo.ValidatedAt = req.User.PrimaryEmailAddressValidatedAt - break - } - } - - // If the old email was not found on the contact info list it replaces everything with the new email. - if !foundOldEmailAddress { - contactInfo = []*ttnpb.ContactInfo{{ - ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, - Value: req.User.PrimaryEmailAddress, - ValidatedAt: req.User.PrimaryEmailAddressValidatedAt, - }} - } - contactInfo, err = st.SetContactInfo(ctx, req.User.GetIds(), contactInfo) + // When updated by an admin, the user's primary email address remains valid. + if updatedByAdmin && ttnpb.HasAnyField(req.FieldMask.GetPaths(), "primary_email_address") { + oldUser, err := st.GetUser(ctx, req.User.GetIds(), []string{"primary_email_address_validated_at"}) if err != nil { return err } + req.User.PrimaryEmailAddressValidatedAt = oldUser.PrimaryEmailAddressValidatedAt + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.GetPaths(), "primary_email_address_validated_at") } - // In order to avoid double patching the contact info list we exclude it from the update. - // TODO: remove the separate store operations for ContactInfo - // (https://github.com/TheThingsNetwork/lorawan-stack/issues/6515). - req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") - usr, err = st.UpdateUser(ctx, req.User, req.FieldMask.GetPaths()) if err != nil { return err } - if updateContactInfo || updatePrimaryEmailAddress { - usr.ContactInfo = contactInfo - } - return nil }) if err != nil { @@ -801,12 +717,8 @@ func (is *IdentityServer) purgeUser(ctx context.Context, ids *ttnpb.UserIdentifi return nil, errAdminsPurgeUsers.New() } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { - err := st.DeleteEntityContactInfo(ctx, ids) - if err != nil { - return err - } // Delete related API keys before purging the user. - err = st.DeleteEntityAPIKeys(ctx, ids.GetEntityIdentifiers()) + err := st.DeleteEntityAPIKeys(ctx, ids.GetEntityIdentifiers()) if err != nil { return err } From b78e0692078feaba5837d38cf5ac46cd0cdea791 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 18:03:30 -0300 Subject: [PATCH 10/30] is: Remove ContactInfo from application registry write operations --- pkg/identityserver/application_registry.go | 28 +++------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/pkg/identityserver/application_registry.go b/pkg/identityserver/application_registry.go index ff047af2e2..54c714a268 100644 --- a/pkg/identityserver/application_registry.go +++ b/pkg/identityserver/application_registry.go @@ -24,6 +24,7 @@ import ( "go.thethings.network/lorawan-stack/v3/pkg/events" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/blocklist" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" + "go.thethings.network/lorawan-stack/v3/pkg/rpcmiddleware/warning" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "google.golang.org/protobuf/types/known/emptypb" ) @@ -117,9 +118,6 @@ func (is *IdentityServer) createApplication( //nolint:gocyclo ); err != nil { return nil, err } - if err := validateContactInfo(req.Application.ContactInfo); err != nil { - return nil, err - } err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { app, err = st.CreateApplication(ctx, req.Application) if err != nil { @@ -133,13 +131,6 @@ func (is *IdentityServer) createApplication( //nolint:gocyclo ); err != nil { return err } - if len(req.Application.ContactInfo) > 0 { - cleanContactInfo(req.Application.ContactInfo) - app.ContactInfo, err = st.SetContactInfo(ctx, app.GetIds(), req.Application.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -301,9 +292,8 @@ func (is *IdentityServer) updateApplication( req.FieldMask = ttnpb.FieldMask(updatePaths...) } if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - if err := validateContactInfo(req.Application.ContactInfo); err != nil { - return nil, err - } + warning.Add(ctx, "Contact info is deprecated and will be removed in the next major release") + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") } if err := is.validateContactInfoRestrictions( @@ -331,13 +321,6 @@ func (is *IdentityServer) updateApplication( if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - cleanContactInfo(req.Application.ContactInfo) - app.ContactInfo, err = st.SetContactInfo(ctx, app.GetIds(), req.Application.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -433,11 +416,6 @@ func (is *IdentityServer) purgeApplication( if err != nil { return err } - // delete related contact info before purging the application - err = st.DeleteEntityContactInfo(ctx, ids) - if err != nil { - return err - } return st.PurgeApplication(ctx, ids) }) if err != nil { From 6478bb37f145eedc06ba1170b35e9902d58387d8 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 18:09:34 -0300 Subject: [PATCH 11/30] is: Remove ContactInfo from client registry write operations --- pkg/identityserver/client_registry.go | 28 +++------------------------ 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/pkg/identityserver/client_registry.go b/pkg/identityserver/client_registry.go index c7ef54ec6a..9f172af0b2 100644 --- a/pkg/identityserver/client_registry.go +++ b/pkg/identityserver/client_registry.go @@ -24,6 +24,7 @@ import ( "go.thethings.network/lorawan-stack/v3/pkg/events" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/blocklist" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" + "go.thethings.network/lorawan-stack/v3/pkg/rpcmiddleware/warning" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "google.golang.org/protobuf/types/known/emptypb" ) @@ -104,9 +105,6 @@ func (is *IdentityServer) createClient( //nolint:gocyclo } else if err := validateCollaboratorEqualsContact(req.Collaborator, req.Client.TechnicalContact); err != nil { return nil, err } - if err := validateContactInfo(req.Client.ContactInfo); err != nil { - return nil, err - } secret := req.Client.Secret if secret == "" { @@ -141,13 +139,6 @@ func (is *IdentityServer) createClient( //nolint:gocyclo ); err != nil { return err } - if len(req.Client.ContactInfo) > 0 { - cleanContactInfo(req.Client.ContactInfo) - cli.ContactInfo, err = st.SetContactInfo(ctx, cli.GetIds(), req.Client.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -308,9 +299,8 @@ func (is *IdentityServer) updateClient( req.FieldMask = ttnpb.FieldMask(updatePaths...) } if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - if err := validateContactInfo(req.Client.ContactInfo); err != nil { - return nil, err - } + warning.Add(ctx, "Contact info is deprecated and will be removed in the next major release") + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") } req.FieldMask.Paths = ttnpb.FlattenPaths( req.FieldMask.Paths, @@ -351,13 +341,6 @@ func (is *IdentityServer) updateClient( if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - cleanContactInfo(req.Client.ContactInfo) - cli.ContactInfo, err = st.SetContactInfo(ctx, cli.Ids, req.Client.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -435,11 +418,6 @@ func (is *IdentityServer) purgeClient(ctx context.Context, ids *ttnpb.ClientIden if err != nil { return err } - // delete related contact info before purging the client - err = st.DeleteEntityContactInfo(ctx, ids) - if err != nil { - return err - } return st.PurgeClient(ctx, ids) }) if err != nil { From 6f1f258e715f78cf03c644293c76316a424e6b10 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 18:10:30 -0300 Subject: [PATCH 12/30] is: Remove ContactInfo from gateway registry write operations --- pkg/identityserver/gateway_registry.go | 28 +++----------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/pkg/identityserver/gateway_registry.go b/pkg/identityserver/gateway_registry.go index 41921fd836..c1837a435a 100644 --- a/pkg/identityserver/gateway_registry.go +++ b/pkg/identityserver/gateway_registry.go @@ -24,6 +24,7 @@ import ( "go.thethings.network/lorawan-stack/v3/pkg/identityserver/blocklist" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" "go.thethings.network/lorawan-stack/v3/pkg/log" + "go.thethings.network/lorawan-stack/v3/pkg/rpcmiddleware/warning" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/types" storeutil "go.thethings.network/lorawan-stack/v3/pkg/util/store" @@ -125,9 +126,6 @@ func (is *IdentityServer) createGateway( // nolint:gocyclo } else if err := validateCollaboratorEqualsContact(req.Collaborator, req.Gateway.TechnicalContact); err != nil { return nil, err } - if err := validateContactInfo(reqGtw.ContactInfo); err != nil { - return nil, err - } if len(reqGtw.FrequencyPlanIds) == 0 && reqGtw.FrequencyPlanId != "" { reqGtw.FrequencyPlanIds = []string{reqGtw.FrequencyPlanId} @@ -190,13 +188,6 @@ func (is *IdentityServer) createGateway( // nolint:gocyclo ); err != nil { return err } - if len(reqGtw.ContactInfo) > 0 { - cleanContactInfo(reqGtw.ContactInfo) - gtw.ContactInfo, err = st.SetContactInfo(ctx, gtw.GetIds(), reqGtw.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -545,9 +536,8 @@ func (is *IdentityServer) updateGateway( // nolint:gocyclo req.FieldMask = ttnpb.FieldMask(updatePaths...) } if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - if err := validateContactInfo(reqGtw.ContactInfo); err != nil { - return nil, err - } + warning.Add(ctx, "Contact info is deprecated and will be removed in the next major release") + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") } req.FieldMask.Paths = ttnpb.FlattenPaths( req.FieldMask.Paths, @@ -638,13 +628,6 @@ func (is *IdentityServer) updateGateway( // nolint:gocyclo if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - cleanContactInfo(reqGtw.ContactInfo) - gtw.ContactInfo, err = st.SetContactInfo(ctx, gtw.GetIds(), reqGtw.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -722,11 +705,6 @@ func (is *IdentityServer) purgeGateway(ctx context.Context, ids *ttnpb.GatewayId if err != nil { return err } - // delete related contact info before purging the gateway - err = st.DeleteEntityContactInfo(ctx, ids) - if err != nil { - return err - } return st.PurgeGateway(ctx, ids) }) if err != nil { From 54e6d93072f1412e336b2e3ffffc334a9e451d5e Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:17:17 -0300 Subject: [PATCH 13/30] is: Remove ContactInfo from organization registry write operations --- pkg/identityserver/organization_registry.go | 29 +++------------------ 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/pkg/identityserver/organization_registry.go b/pkg/identityserver/organization_registry.go index d5893105be..aabbcfe545 100644 --- a/pkg/identityserver/organization_registry.go +++ b/pkg/identityserver/organization_registry.go @@ -23,6 +23,7 @@ import ( "go.thethings.network/lorawan-stack/v3/pkg/events" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/blocklist" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" + "go.thethings.network/lorawan-stack/v3/pkg/rpcmiddleware/warning" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "google.golang.org/protobuf/types/known/emptypb" ) @@ -104,9 +105,6 @@ func (is *IdentityServer) createOrganization( } else if err := validateCollaboratorEqualsContact(req.Collaborator, req.Organization.TechnicalContact); err != nil { return nil, err } - if err := validateContactInfo(req.Organization.ContactInfo); err != nil { - return nil, err - } err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { org, err = st.CreateOrganization(ctx, req.Organization) @@ -121,13 +119,6 @@ func (is *IdentityServer) createOrganization( ); err != nil { return err } - if len(req.Organization.ContactInfo) > 0 { - cleanContactInfo(req.Organization.ContactInfo) - org.ContactInfo, err = st.SetContactInfo(ctx, org.GetIds(), req.Organization.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -268,9 +259,8 @@ func (is *IdentityServer) updateOrganization( req.FieldMask = ttnpb.FieldMask(updatePaths...) } if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - if err := validateContactInfo(req.Organization.ContactInfo); err != nil { - return nil, err - } + warning.Add(ctx, "Contact info is deprecated and will be removed in the next major release") + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") } if err := is.validateContactInfoRestrictions( @@ -298,13 +288,6 @@ func (is *IdentityServer) updateOrganization( if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - cleanContactInfo(req.Organization.ContactInfo) - org.ContactInfo, err = st.SetContactInfo(ctx, org.Ids, req.Organization.ContactInfo) - if err != nil { - return err - } - } return nil }) if err != nil { @@ -371,12 +354,8 @@ func (is *IdentityServer) purgeOrganization( return nil, errAdminsPurgeOrganizations.New() } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { - err := st.DeleteEntityContactInfo(ctx, ids) - if err != nil { - return err - } // Delete related API keys before purging the organization. - err = st.DeleteEntityAPIKeys(ctx, ids.GetEntityIdentifiers()) + err := st.DeleteEntityAPIKeys(ctx, ids.GetEntityIdentifiers()) if err != nil { return err } From 6c98bae07c7a8db9e120f474af2a13ba3bda07f7 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:18:50 -0300 Subject: [PATCH 14/30] is: Add getContactsFromEntity function to contact registry --- pkg/identityserver/contact_info_registry.go | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/pkg/identityserver/contact_info_registry.go b/pkg/identityserver/contact_info_registry.go index c8435e6a8f..78600be273 100644 --- a/pkg/identityserver/contact_info_registry.go +++ b/pkg/identityserver/contact_info_registry.go @@ -43,6 +43,65 @@ var ( ) ) +// getContactsFromEntity fetches the administrative and technical contacts from the provided entity and returns a slice +// of ContactInfo pointers. The usage of this function should be restricted to the replacement of read operations +// related to the deprecated `ContactInfo`. +func getContactsFromEntity[ + X interface { + GetAdministrativeContact() *ttnpb.OrganizationOrUserIdentifiers + GetTechnicalContact() *ttnpb.OrganizationOrUserIdentifiers + }, +]( + ctx context.Context, id X, st store.Store, +) ([]*ttnpb.ContactInfo, error) { + orgMask := []string{"administrative_contact", "technical_contact"} + contacts := make([]*ttnpb.ContactInfo, 0, 2) + + fn := func(id *ttnpb.OrganizationOrUserIdentifiers, isAdminContact bool) error { + usrID := id.GetUserIds() + var contactType ttnpb.ContactType + if !isAdminContact { + contactType = ttnpb.ContactType_CONTACT_TYPE_TECHNICAL + } + + if orgID := id.GetOrganizationIds(); orgID != nil { + org, err := st.GetOrganization(ctx, orgID, orgMask) + if err != nil { + return err + } + + if isAdminContact { + usrID = org.AdministrativeContact.GetUserIds() + } else { + usrID = org.TechnicalContact.GetUserIds() + } + } + + usr, err := st.GetUser(ctx, usrID, []string{"primary_email_address"}) + if err != nil { + return err + } + + contacts = append(contacts, &ttnpb.ContactInfo{ + ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, + ContactType: contactType, + ValidatedAt: usr.PrimaryEmailAddressValidatedAt, + Value: usr.PrimaryEmailAddress, + }) + + return nil + } + + if err := fn(id.GetAdministrativeContact(), true); err != nil { + return nil, err + } + if err := fn(id.GetTechnicalContact(), false); err != nil { + return nil, err + } + + return contacts, nil +} + // validateContactInfoRestrictions fetches the auth info from the context and validates if the caller ID matches the // provided `ids` in the parameters. The usage of this function should be restricted to testing the administrative and // technical contacts in methods belonging to each entity registry. From f37c044279f469c6788260b598be5ea5e290cc19 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:21:33 -0300 Subject: [PATCH 15/30] is: Update SearchUsers in registry_search --- pkg/identityserver/registry_search.go | 24 +++++++++++++++++++++- pkg/identityserver/registry_search_test.go | 15 ++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/registry_search.go b/pkg/identityserver/registry_search.go index 432a82cc01..b5d685a5f2 100644 --- a/pkg/identityserver/registry_search.go +++ b/pkg/identityserver/registry_search.go @@ -380,7 +380,9 @@ func (rs *registrySearch) SearchOrganizations(ctx context.Context, req *ttnpb.Se return res, nil } -func (rs *registrySearch) SearchUsers(ctx context.Context, req *ttnpb.SearchUsersRequest) (*ttnpb.Users, error) { +func (rs *registrySearch) SearchUsers( + ctx context.Context, req *ttnpb.SearchUsersRequest, +) (*ttnpb.Users, error) { authInfo, err := rs.authInfo(ctx) if err != nil { return nil, err @@ -389,6 +391,12 @@ func (rs *registrySearch) SearchUsers(ctx context.Context, req *ttnpb.SearchUser return nil, errSearchForbidden.New() } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = append(req.FieldMask.Paths, "primary_email_address", "primary_email_address_validated_at") + } + var searchFields []string if req.IdContains != "" { searchFields = append(searchFields, "ids") @@ -446,6 +454,20 @@ func (rs *registrySearch) SearchUsers(ctx context.Context, req *ttnpb.SearchUser if err != nil { return nil, err } + + // Add ContactInfo to the response if its present in the field mask. + if contactInfoInPath { + for _, usr := range res.Users { + usr.ContactInfo = append(usr.ContactInfo, &ttnpb.ContactInfo{ + ContactType: ttnpb.ContactType_CONTACT_TYPE_OTHER, + ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, + Value: usr.PrimaryEmailAddress, + ValidatedAt: usr.PrimaryEmailAddressValidatedAt, + Public: false, + }) + } + } + return res, nil } diff --git a/pkg/identityserver/registry_search_test.go b/pkg/identityserver/registry_search_test.go index 11aa0f6a9d..23c75417e3 100644 --- a/pkg/identityserver/registry_search_test.go +++ b/pkg/identityserver/registry_search_test.go @@ -320,6 +320,21 @@ func TestRegistrySearchDeletedEntities(t *testing.T) { // nolint:gocyclo a.So(got.Users, should.HaveLength, deletedAmount) } }) + t.Run("Read ContactInfo", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + got, err := cli.SearchUsers(ctx, &ttnpb.SearchUsersRequest{ + FieldMask: ttnpb.FieldMask("contact_info"), + }, adminUsrCreds) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + // The `+2` refers to the two non-deleted users created at the beginning of the test. + a.So(got.Users, should.HaveLength, (notDeletedAmount + 2)) + for _, user := range got.Users { + a.So(user.ContactInfo, should.HaveLength, 1) + a.So(user.ContactInfo[0].Value, should.Equal, user.Ids.UserId+"@example.com") + } + } + }) + }) }, withPrivateTestDatabase(p)) } From 68985ec39abf5342e6f181ab964ca5342569c816 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:23:20 -0300 Subject: [PATCH 16/30] is: Update SearchApplications in registry_search --- pkg/identityserver/registry_search.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/registry_search.go b/pkg/identityserver/registry_search.go index b5d685a5f2..dce07dfeb0 100644 --- a/pkg/identityserver/registry_search.go +++ b/pkg/identityserver/registry_search.go @@ -32,7 +32,9 @@ type registrySearch struct { var errSearchForbidden = errors.DefinePermissionDenied("search_forbidden", "search is forbidden") -func (rs *registrySearch) SearchApplications(ctx context.Context, req *ttnpb.SearchApplicationsRequest) (*ttnpb.Applications, error) { +func (rs *registrySearch) SearchApplications( + ctx context.Context, req *ttnpb.SearchApplicationsRequest, +) (*ttnpb.Applications, error) { authInfo, err := rs.authInfo(ctx) if err != nil { return nil, err @@ -45,6 +47,12 @@ func (rs *registrySearch) SearchApplications(ctx context.Context, req *ttnpb.Sea member = nil } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = append(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } + var searchFields []string if req.IdContains != "" { searchFields = append(searchFields, "ids") @@ -98,6 +106,16 @@ func (rs *registrySearch) SearchApplications(ctx context.Context, req *ttnpb.Sea if err != nil { return err } + + if contactInfoInPath { + for _, app := range res.Applications { + app.ContactInfo, err = getContactsFromEntity(ctx, app, st) + if err != nil { + return err + } + } + } + return nil }) if err != nil { From 67a8c268589f9a0585bee66e802f3c34475445e3 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:24:59 -0300 Subject: [PATCH 17/30] is: Update SearchClients in registry_search --- pkg/identityserver/registry_search.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/registry_search.go b/pkg/identityserver/registry_search.go index dce07dfeb0..2daf9cf9ae 100644 --- a/pkg/identityserver/registry_search.go +++ b/pkg/identityserver/registry_search.go @@ -132,7 +132,9 @@ func (rs *registrySearch) SearchApplications( return res, nil } -func (rs *registrySearch) SearchClients(ctx context.Context, req *ttnpb.SearchClientsRequest) (*ttnpb.Clients, error) { +func (rs *registrySearch) SearchClients( + ctx context.Context, req *ttnpb.SearchClientsRequest, +) (*ttnpb.Clients, error) { authInfo, err := rs.authInfo(ctx) if err != nil { return nil, err @@ -145,6 +147,12 @@ func (rs *registrySearch) SearchClients(ctx context.Context, req *ttnpb.SearchCl member = nil } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = append(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } + var searchFields []string if req.IdContains != "" { searchFields = append(searchFields, "ids") @@ -201,6 +209,16 @@ func (rs *registrySearch) SearchClients(ctx context.Context, req *ttnpb.SearchCl if err != nil { return err } + + if contactInfoInPath { + for _, clt := range res.Clients { + clt.ContactInfo, err = getContactsFromEntity(ctx, clt, st) + if err != nil { + return err + } + } + } + return nil }) if err != nil { From 5b5751e4d1fbbce5a3ebda16f60a5614a8d253da Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:26:25 -0300 Subject: [PATCH 18/30] is: Update SearchGateways in registry_search --- pkg/identityserver/registry_search.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/identityserver/registry_search.go b/pkg/identityserver/registry_search.go index 2daf9cf9ae..3a3223d094 100644 --- a/pkg/identityserver/registry_search.go +++ b/pkg/identityserver/registry_search.go @@ -235,7 +235,9 @@ func (rs *registrySearch) SearchClients( return res, nil } -func (rs *registrySearch) SearchGateways(ctx context.Context, req *ttnpb.SearchGatewaysRequest) (*ttnpb.Gateways, error) { +func (rs *registrySearch) SearchGateways( + ctx context.Context, req *ttnpb.SearchGatewaysRequest, +) (*ttnpb.Gateways, error) { authInfo, err := rs.authInfo(ctx) if err != nil { return nil, err @@ -248,6 +250,12 @@ func (rs *registrySearch) SearchGateways(ctx context.Context, req *ttnpb.SearchG member = nil } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = append(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } + // Backwards compatibility for frequency_plan_id field. if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "frequency_plan_id") { if !ttnpb.HasAnyField(req.FieldMask.GetPaths(), "frequency_plan_ids") { @@ -307,6 +315,16 @@ func (rs *registrySearch) SearchGateways(ctx context.Context, req *ttnpb.SearchG if err != nil { return err } + + if contactInfoInPath { + for _, gtw := range res.Gateways { + gtw.ContactInfo, err = getContactsFromEntity(ctx, gtw, st) + if err != nil { + return err + } + } + } + return nil }) if err != nil { From 8ccbc9ead73675072a4a23db77f98ad5eea24e1b Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:28:25 -0300 Subject: [PATCH 19/30] is: Update SearchOrganizations in registry_search --- pkg/identityserver/registry_search.go | 20 +++++++++++++++++++- pkg/identityserver/registry_search_test.go | 1 - 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/registry_search.go b/pkg/identityserver/registry_search.go index 3a3223d094..f6440e8e69 100644 --- a/pkg/identityserver/registry_search.go +++ b/pkg/identityserver/registry_search.go @@ -350,7 +350,9 @@ func (rs *registrySearch) SearchGateways( return res, nil } -func (rs *registrySearch) SearchOrganizations(ctx context.Context, req *ttnpb.SearchOrganizationsRequest) (*ttnpb.Organizations, error) { +func (rs *registrySearch) SearchOrganizations( + ctx context.Context, req *ttnpb.SearchOrganizationsRequest, +) (*ttnpb.Organizations, error) { authInfo, err := rs.authInfo(ctx) if err != nil { return nil, err @@ -363,6 +365,12 @@ func (rs *registrySearch) SearchOrganizations(ctx context.Context, req *ttnpb.Se member = nil } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = append(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } + var searchFields []string if req.IdContains != "" { searchFields = append(searchFields, "ids") @@ -416,6 +424,16 @@ func (rs *registrySearch) SearchOrganizations(ctx context.Context, req *ttnpb.Se if err != nil { return err } + + if contactInfoInPath { + for _, org := range res.Organizations { + org.ContactInfo, err = getContactsFromEntity(ctx, org, st) + if err != nil { + return err + } + } + } + return nil }) if err != nil { diff --git a/pkg/identityserver/registry_search_test.go b/pkg/identityserver/registry_search_test.go index 23c75417e3..e8457cdc73 100644 --- a/pkg/identityserver/registry_search_test.go +++ b/pkg/identityserver/registry_search_test.go @@ -334,7 +334,6 @@ func TestRegistrySearchDeletedEntities(t *testing.T) { // nolint:gocyclo } } }) - }) }, withPrivateTestDatabase(p)) } From 6ff722c85be481941693bba5874dde147cabd5d5 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:33:04 -0300 Subject: [PATCH 20/30] is: Remove ContactInfo from user registry read operations --- pkg/identityserver/user_registry.go | 38 ++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/pkg/identityserver/user_registry.go b/pkg/identityserver/user_registry.go index 850ab58b0b..58609ebe17 100644 --- a/pkg/identityserver/user_registry.go +++ b/pkg/identityserver/user_registry.go @@ -288,6 +288,13 @@ func (is *IdentityServer) getUser(ctx context.Context, req *ttnpb.GetUserRequest } defer func() { usr = usr.PublicSafe() }() } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields( + req.FieldMask.Paths, "primary_email_address", "primary_email_address_validated_at", + ) + } if ttnpb.HasAnyField(ttnpb.TopLevelFields(req.FieldMask.GetPaths()), "profile_picture") { if is.configFromContext(ctx).ProfilePicture.UseGravatar { @@ -309,10 +316,14 @@ func (is *IdentityServer) getUser(ctx context.Context, req *ttnpb.GetUserRequest if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - usr.ContactInfo, err = st.GetContactInfo(ctx, usr.GetIds()) - if err != nil { - return err + if contactInfoInPath { + usr.ContactInfo = []*ttnpb.ContactInfo{ + { + ContactType: ttnpb.ContactType_CONTACT_TYPE_OTHER, + ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, + Value: usr.PrimaryEmailAddress, + ValidatedAt: usr.PrimaryEmailAddressValidatedAt, + }, } } return err @@ -328,6 +339,13 @@ func (is *IdentityServer) listUsers(ctx context.Context, req *ttnpb.ListUsersReq if err = is.RequireAdmin(ctx); err != nil { return nil, err } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields( + req.FieldMask.Paths, "primary_email_address", "primary_email_address_validated_at", + ) + } if req.Deleted { ctx = store.WithSoftDeleted(ctx, true) } @@ -350,6 +368,18 @@ func (is *IdentityServer) listUsers(ctx context.Context, req *ttnpb.ListUsersReq if err != nil { return nil, err } + if contactInfoInPath { + for idx := range users.Users { + users.Users[idx].ContactInfo = []*ttnpb.ContactInfo{ + { + ContactType: ttnpb.ContactType_CONTACT_TYPE_OTHER, + ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, + Value: users.Users[idx].PrimaryEmailAddress, + ValidatedAt: users.Users[idx].PrimaryEmailAddressValidatedAt, + }, + } + } + } return users, nil } From 85c6011a638c428d47cb3e627cb800e8e9044af9 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:35:54 -0300 Subject: [PATCH 21/30] is: Remove ContactInfo from organization registry read operations --- pkg/identityserver/organization_registry.go | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/organization_registry.go b/pkg/identityserver/organization_registry.go index aabbcfe545..3e730436b2 100644 --- a/pkg/identityserver/organization_registry.go +++ b/pkg/identityserver/organization_registry.go @@ -135,6 +135,11 @@ func (is *IdentityServer) getOrganization( if err = is.RequireAuthenticated(ctx); err != nil { return nil, err } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths(ttnpb.OrganizationFieldPathsNested, req.FieldMask, getPaths, nil) if err = rights.RequireOrganization(ctx, req.GetOrganizationIds(), ttnpb.Right_RIGHT_ORGANIZATION_INFO); err != nil { if !ttnpb.HasOnlyAllowedFields(req.FieldMask.GetPaths(), ttnpb.PublicOrganizationFields...) { @@ -147,8 +152,8 @@ func (is *IdentityServer) getOrganization( if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - org.ContactInfo, err = st.GetContactInfo(ctx, org.GetIds()) + if contactInfoInPath { + org.ContactInfo, err = getContactsFromEntity(ctx, org, st) if err != nil { return err } @@ -165,6 +170,11 @@ func (is *IdentityServer) listOrganizations( ctx context.Context, req *ttnpb.ListOrganizationsRequest, ) (orgs *ttnpb.Organizations, err error) { + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths(ttnpb.OrganizationFieldPathsNested, req.FieldMask, getPaths, nil) authInfo, err := is.authInfo(ctx) @@ -229,6 +239,15 @@ func (is *IdentityServer) listOrganizations( if err != nil { return err } + + if contactInfoInPath { + for _, org := range orgs.Organizations { + org.ContactInfo, err = getContactsFromEntity(ctx, org, st) + if err != nil { + return err + } + } + } return nil }) if err != nil { From 8846f4e47a9ebb5939a9eb985b06811e26c16136 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:38:00 -0300 Subject: [PATCH 22/30] is: Remove ContactInfo from gateway registry read operations --- pkg/identityserver/gateway_registry.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/gateway_registry.go b/pkg/identityserver/gateway_registry.go index c1837a435a..5c9a4bc120 100644 --- a/pkg/identityserver/gateway_registry.go +++ b/pkg/identityserver/gateway_registry.go @@ -230,6 +230,11 @@ func (is *IdentityServer) getGateway( // nolint:gocyclo req.FieldMask.Paths = append(req.FieldMask.GetPaths(), "frequency_plan_ids") } } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths( ttnpb.GatewayFieldPathsNested, req.FieldMask, @@ -255,8 +260,8 @@ func (is *IdentityServer) getGateway( // nolint:gocyclo if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - gtw.ContactInfo, err = st.GetContactInfo(ctx, gtw.GetIds()) + if contactInfoInPath { + gtw.ContactInfo, err = getContactsFromEntity(ctx, gtw, st) if err != nil { return err } @@ -351,6 +356,11 @@ func (is *IdentityServer) listGateways( // nolint:gocyclo req.FieldMask.Paths = append(req.FieldMask.GetPaths(), "frequency_plan_ids") } } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths( ttnpb.GatewayFieldPathsNested, req.FieldMask, @@ -420,6 +430,15 @@ func (is *IdentityServer) listGateways( // nolint:gocyclo if err != nil { return err } + + if contactInfoInPath { + for _, gtw := range gtws.Gateways { + gtw.ContactInfo, err = getContactsFromEntity(ctx, gtw, st) + if err != nil { + return err + } + } + } return nil }) if err != nil { From d5aac2bf093cb8a964e5b82223969efe45c2ad47 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:39:45 -0300 Subject: [PATCH 23/30] is: Remove ContactInfo from client registry read operations --- pkg/identityserver/client_registry.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/client_registry.go b/pkg/identityserver/client_registry.go index 9f172af0b2..ac3a825c9e 100644 --- a/pkg/identityserver/client_registry.go +++ b/pkg/identityserver/client_registry.go @@ -177,6 +177,11 @@ func (is *IdentityServer) getClient(ctx context.Context, req *ttnpb.GetClientReq if err = is.RequireAuthenticated(ctx); err != nil { return nil, err } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths(ttnpb.ClientFieldPathsNested, req.FieldMask, getPaths, nil) if err = rights.RequireClient(ctx, req.GetClientIds(), ttnpb.Right_RIGHT_CLIENT_INFO); err != nil { if !ttnpb.HasOnlyAllowedFields(req.FieldMask.GetPaths(), ttnpb.PublicClientFields...) { @@ -189,8 +194,8 @@ func (is *IdentityServer) getClient(ctx context.Context, req *ttnpb.GetClientReq if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - cli.ContactInfo, err = st.GetContactInfo(ctx, cli.GetIds()) + if contactInfoInPath { + cli.ContactInfo, err = getContactsFromEntity(ctx, cli, st) if err != nil { return err } @@ -207,6 +212,11 @@ func (is *IdentityServer) listClients( ctx context.Context, req *ttnpb.ListClientsRequest, ) (clis *ttnpb.Clients, err error) { + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths(ttnpb.ClientFieldPathsNested, req.FieldMask, getPaths, nil) authInfo, err := is.authInfo(ctx) @@ -271,6 +281,14 @@ func (is *IdentityServer) listClients( if err != nil { return err } + if contactInfoInPath { + for _, cli := range clis.Clients { + cli.ContactInfo, err = getContactsFromEntity(ctx, cli, st) + if err != nil { + return err + } + } + } return nil }) if err != nil { From b002b0e996b26a6086b28d07e6fe05fa12b1ee5c Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 19:41:01 -0300 Subject: [PATCH 24/30] is: Remove ContactInfo from application registry read operations --- pkg/identityserver/application_registry.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/application_registry.go b/pkg/identityserver/application_registry.go index 54c714a268..b4b2bea1b1 100644 --- a/pkg/identityserver/application_registry.go +++ b/pkg/identityserver/application_registry.go @@ -147,6 +147,11 @@ func (is *IdentityServer) getApplication( if err = is.RequireAuthenticated(ctx); err != nil { return nil, err } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths(ttnpb.ApplicationFieldPathsNested, req.FieldMask, getPaths, nil) if err = rights.RequireApplication(ctx, req.GetApplicationIds(), ttnpb.Right_RIGHT_APPLICATION_INFO); err != nil { if !ttnpb.HasOnlyAllowedFields(req.FieldMask.GetPaths(), ttnpb.PublicApplicationFields...) { @@ -159,8 +164,8 @@ func (is *IdentityServer) getApplication( if err != nil { return err } - if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") { - app.ContactInfo, err = st.GetContactInfo(ctx, app.GetIds()) + if contactInfoInPath { + app.ContactInfo, err = getContactsFromEntity(ctx, app, st) if err != nil { return err } @@ -177,6 +182,11 @@ func (is *IdentityServer) listApplications( // nolint:gocyclo ctx context.Context, req *ttnpb.ListApplicationsRequest, ) (apps *ttnpb.Applications, err error) { + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") + if contactInfoInPath { + req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") + req.FieldMask.Paths = ttnpb.AddFields(req.FieldMask.Paths, "administrative_contact", "technical_contact") + } req.FieldMask = cleanFieldMaskPaths(ttnpb.ApplicationFieldPathsNested, req.FieldMask, getPaths, nil) authInfo, err := is.authInfo(ctx) @@ -262,6 +272,14 @@ func (is *IdentityServer) listApplications( // nolint:gocyclo if err != nil { return err } + if contactInfoInPath { + for _, app := range apps.Applications { + app.ContactInfo, err = getContactsFromEntity(ctx, app, st) + if err != nil { + return err + } + } + } return nil }) if err != nil { From 4390407c3716b26dc6481d80bd6b28f7b1da7e38 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 21:56:10 -0300 Subject: [PATCH 25/30] is: Update ContactInfo test case in user registry tests --- pkg/identityserver/storetest/user_store.go | 6 ++-- pkg/identityserver/user_registry_test.go | 36 ++++++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pkg/identityserver/storetest/user_store.go b/pkg/identityserver/storetest/user_store.go index 0372c39045..6e0136feb3 100644 --- a/pkg/identityserver/storetest/user_store.go +++ b/pkg/identityserver/storetest/user_store.go @@ -39,7 +39,7 @@ func (st *StoreTest) TestUserStoreCRUD(t *T) { } defer s.Close() - mask := fieldMask(ttnpb.UserFieldPathsTopLevel...) + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.UserFieldPathsTopLevel...), "contact_info") picture := &ttnpb.Picture{ Embedded: &ttnpb.Picture_Embedded{ @@ -423,6 +423,8 @@ func (st *StoreTest) TestUserStorePagination(t *T) { } defer s.Close() + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.UserFieldPathsTopLevel...), "contact_info") + t.Run("FindUsers_Paginated", func(t *T) { a, ctx := test.New(t) @@ -430,7 +432,7 @@ func (st *StoreTest) TestUserStorePagination(t *T) { for _, page := range []uint32{1, 2, 3, 4} { paginateCtx := store.WithPagination(ctx, 2, page, &total) - got, err := s.FindUsers(paginateCtx, nil, fieldMask(ttnpb.UserFieldPathsTopLevel...)) + got, err := s.FindUsers(paginateCtx, nil, mask) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { if page == 4 { a.So(got, should.HaveLength, 1) diff --git a/pkg/identityserver/user_registry_test.go b/pkg/identityserver/user_registry_test.go index 0eef1d63f3..9ae4b5b8d9 100644 --- a/pkg/identityserver/user_registry_test.go +++ b/pkg/identityserver/user_registry_test.go @@ -254,13 +254,6 @@ func TestUsersCRUD(t *testing.T) { usr1.PrimaryEmailAddress = "user-1@email.com" validatedAtTime := time.Now().Truncate(time.Millisecond) usr1.PrimaryEmailAddressValidatedAt = ttnpb.ProtoTime(&validatedAtTime) - // NOTE: Remove this when the deprecated field is removed. - // (https://github.com/TheThingsIndustries/lorawan-stack/issues/3830) - usr1.ContactInfo = append(usr1.ContactInfo, &ttnpb.ContactInfo{ // nolint:staticcheck - ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, - Value: usr1.PrimaryEmailAddress, - ValidatedAt: usr1.PrimaryEmailAddressValidatedAt, - }) key, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) creds := rpcCreds(key) @@ -300,6 +293,29 @@ func TestUsersCRUD(t *testing.T) { a.So(errors.IsPermissionDenied(err), should.BeTrue) a.So(got, should.BeNil) } + + // TODO: Remove (https://github.com/TheThingsNetwork/lorawan-stack/issues/6804) + t.Run("Contact_info fieldmask", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + got, err := reg.Get(ctx, &ttnpb.GetUserRequest{ + UserIds: usr1.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, creds) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 1) + a.So(got.ContactInfo[0].Value, should.Equal, usr1.PrimaryEmailAddress) + } + + // Testing the `PublicSafe` method, which should not return the contact_info's email address when the + // caller does not have the appropriate rights. + got, err = reg.Get(ctx, &ttnpb.GetUserRequest{ + UserIds: usr1.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, credsWithoutRights) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 0) + } + }) }) t.Run("Update", func(t *testing.T) { // nolint:paralleltest @@ -367,9 +383,6 @@ func TestUsersCRUD(t *testing.T) { if a.So(err, should.BeNil) { a.So(got.PrimaryEmailAddress, should.Equal, "new-user-email@email.com") a.So(got.PrimaryEmailAddressValidatedAt, should.NotBeNil) - a.So(got.ContactInfo, should.HaveLength, 1) // nolint:staticcheck - a.So(got.ContactInfo[0].Value, should.Equal, "new-user-email@email.com") // nolint:staticcheck - a.So(got.ContactInfo[0].ValidatedAt, should.NotBeNil) // nolint:staticcheck } }) @@ -385,9 +398,6 @@ func TestUsersCRUD(t *testing.T) { if a.So(err, should.BeNil) { a.So(got.PrimaryEmailAddress, should.Equal, "second-new-user-email@email.com") a.So(got.PrimaryEmailAddressValidatedAt, should.BeNil) - a.So(got.ContactInfo, should.HaveLength, 1) // nolint:staticcheck,lll - a.So(got.ContactInfo[0].Value, should.Equal, "second-new-user-email@email.com") // nolint:staticcheck,lll - a.So(got.ContactInfo[0].ValidatedAt, should.BeNil) // nolint:staticcheck,lll } }) }) From 88ba98ea9e631549c472a7c7a5a46a48dd404e2d Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 22:12:19 -0300 Subject: [PATCH 26/30] is: Update ContactInfo test case in application registry tests --- .../application_registry_test.go | 26 +++++++++++++++++++ .../storetest/application_store.go | 6 +++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/application_registry_test.go b/pkg/identityserver/application_registry_test.go index 0966e92478..3fc372ecf8 100644 --- a/pkg/identityserver/application_registry_test.go +++ b/pkg/identityserver/application_registry_test.go @@ -176,6 +176,32 @@ func TestApplicationsCRUD(t *testing.T) { a.So(errors.IsPermissionDenied(err), should.BeTrue) } + // TODO: Remove (https://github.com/TheThingsNetwork/lorawan-stack/issues/6804) + t.Run("Contact_info fieldmask", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + got, err := reg.Get(ctx, &ttnpb.GetApplicationRequest{ + ApplicationIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, creds) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 2) + a.So(got.ContactInfo[0].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[0].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_OTHER) + a.So(got.ContactInfo[1].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[1].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_TECHNICAL) + } + + // Testing the `PublicSafe` method, which should not return the contact_info's email address when the caller + // does not have the appropriate rights. + got, err = reg.Get(ctx, &ttnpb.GetApplicationRequest{ + ApplicationIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, credsWithoutRights) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 0) + } + }) + updated, err := reg.Update(ctx, &ttnpb.UpdateApplicationRequest{ Application: &ttnpb.Application{ Ids: created.GetIds(), diff --git a/pkg/identityserver/storetest/application_store.go b/pkg/identityserver/storetest/application_store.go index 6499d8715f..b67aab1bd3 100644 --- a/pkg/identityserver/storetest/application_store.go +++ b/pkg/identityserver/storetest/application_store.go @@ -41,7 +41,7 @@ func (st *StoreTest) TestApplicationStoreCRUD(t *T) { } defer s.Close() - mask := fieldMask(ttnpb.ApplicationFieldPathsTopLevel...) + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.ApplicationFieldPathsTopLevel...), "contact_info") var created *ttnpb.Application @@ -335,6 +335,8 @@ func (st *StoreTest) TestApplicationStorePagination(t *T) { } defer s.Close() + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.ApplicationFieldPathsTopLevel...), "contact_info") + t.Run("FindApplications_Paginated", func(t *T) { a, ctx := test.New(t) @@ -342,7 +344,7 @@ func (st *StoreTest) TestApplicationStorePagination(t *T) { for _, page := range []uint32{1, 2, 3, 4} { paginateCtx := store.WithPagination(ctx, 2, page, &total) - got, err := s.FindApplications(paginateCtx, nil, fieldMask(ttnpb.ApplicationFieldPathsTopLevel...)) + got, err := s.FindApplications(paginateCtx, nil, mask) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { if page == 4 { a.So(got, should.HaveLength, 1) From 845f11920a22f558862f0b2f0cfbf5b3e21bfefb Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 22:14:24 -0300 Subject: [PATCH 27/30] is: Update ContactInfo test case in client registry tests --- pkg/identityserver/client_registry_test.go | 26 ++++++++++++++++++++ pkg/identityserver/storetest/client_store.go | 6 +++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/client_registry_test.go b/pkg/identityserver/client_registry_test.go index 101b04f0c7..5622363832 100644 --- a/pkg/identityserver/client_registry_test.go +++ b/pkg/identityserver/client_registry_test.go @@ -166,6 +166,32 @@ func TestClientsCRUD(t *testing.T) { a.So(errors.IsPermissionDenied(err), should.BeTrue) } + // TODO: Remove (https://github.com/TheThingsNetwork/lorawan-stack/issues/6804) + t.Run("Contact_info fieldmask", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + got, err := reg.Get(ctx, &ttnpb.GetClientRequest{ + ClientIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, creds) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 2) + a.So(got.ContactInfo[0].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[0].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_OTHER) + a.So(got.ContactInfo[1].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[1].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_TECHNICAL) + } + + // Testing the `PublicSafe` method, which should not return the contact_info's email address when the caller + // does not have the appropriate rights. + got, err = reg.Get(ctx, &ttnpb.GetClientRequest{ + ClientIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, credsWithoutRights) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 0) + } + }) + updated, err := reg.Update(ctx, &ttnpb.UpdateClientRequest{ Client: &ttnpb.Client{ Ids: created.GetIds(), diff --git a/pkg/identityserver/storetest/client_store.go b/pkg/identityserver/storetest/client_store.go index 1f47739ab1..3d4c8d3d6b 100644 --- a/pkg/identityserver/storetest/client_store.go +++ b/pkg/identityserver/storetest/client_store.go @@ -41,7 +41,7 @@ func (st *StoreTest) TestClientStoreCRUD(t *T) { } defer s.Close() - mask := fieldMask(ttnpb.ClientFieldPathsTopLevel...) + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.ClientFieldPathsTopLevel...), "contact_info") var created *ttnpb.Client @@ -323,6 +323,8 @@ func (st *StoreTest) TestClientStorePagination(t *T) { } defer s.Close() + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.ClientFieldPathsTopLevel...), "contact_info") + t.Run("FindClients_Paginated", func(t *T) { a, ctx := test.New(t) @@ -330,7 +332,7 @@ func (st *StoreTest) TestClientStorePagination(t *T) { for _, page := range []uint32{1, 2, 3, 4} { paginateCtx := store.WithPagination(ctx, 2, page, &total) - got, err := s.FindClients(paginateCtx, nil, fieldMask(ttnpb.ClientFieldPathsTopLevel...)) + got, err := s.FindClients(paginateCtx, nil, mask) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { if page == 4 { a.So(got, should.HaveLength, 1) From 00756d722444d767d85c6f44cebe6c6dd9b03850 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 22:19:44 -0300 Subject: [PATCH 28/30] is: Update ContactInfo test case in gateway registry tests --- pkg/identityserver/gateway_registry_test.go | 26 +++++++++++++++++++ pkg/identityserver/storetest/gateway_store.go | 6 +++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/gateway_registry_test.go b/pkg/identityserver/gateway_registry_test.go index d6461dba8a..f543bbfeec 100644 --- a/pkg/identityserver/gateway_registry_test.go +++ b/pkg/identityserver/gateway_registry_test.go @@ -198,6 +198,32 @@ func TestGatewaysCRUD(t *testing.T) { a.So(errors.IsPermissionDenied(err), should.BeTrue) } + // TODO: Remove (https://github.com/TheThingsNetwork/lorawan-stack/issues/6804) + t.Run("Contact_info fieldmask", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + got, err := reg.Get(ctx, &ttnpb.GetGatewayRequest{ + GatewayIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, creds) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 2) + a.So(got.ContactInfo[0].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[0].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_OTHER) + a.So(got.ContactInfo[1].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[1].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_TECHNICAL) + } + + // Testing the `PublicSafe` method, which should not return the contact_info's email address when the caller + // does not have the appropriate rights. + got, err = reg.Get(ctx, &ttnpb.GetGatewayRequest{ + GatewayIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, credsWithoutRights) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 0) + } + }) + updated, err := reg.Update(ctx, &ttnpb.UpdateGatewayRequest{ Gateway: &ttnpb.Gateway{ Ids: created.GetIds(), diff --git a/pkg/identityserver/storetest/gateway_store.go b/pkg/identityserver/storetest/gateway_store.go index 24e51174b7..c9389e80d6 100644 --- a/pkg/identityserver/storetest/gateway_store.go +++ b/pkg/identityserver/storetest/gateway_store.go @@ -47,7 +47,7 @@ func (st *StoreTest) TestGatewayStoreCRUD(t *T) { defer s.Close() start := time.Now().Truncate(time.Second) - mask := fieldMask(ttnpb.GatewayFieldPathsTopLevel...) + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.GatewayFieldPathsTopLevel...), "contact_info") eui := &types.EUI64{1, 2, 3, 4, 5, 6, 7, 8} antenna := &ttnpb.GatewayAntenna{ @@ -519,6 +519,8 @@ func (st *StoreTest) TestGatewayStorePagination(t *T) { } defer s.Close() + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.GatewayFieldPathsTopLevel...), "contact_info") + t.Run("FindGateways_Paginated", func(t *T) { a, ctx := test.New(t) @@ -526,7 +528,7 @@ func (st *StoreTest) TestGatewayStorePagination(t *T) { for _, page := range []uint32{1, 2, 3, 4} { paginateCtx := store.WithPagination(ctx, 2, page, &total) - got, err := s.FindGateways(paginateCtx, nil, fieldMask(ttnpb.GatewayFieldPathsTopLevel...)) + got, err := s.FindGateways(paginateCtx, nil, mask) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { if page == 4 { a.So(got, should.HaveLength, 1) From c72923652e526edc7842fff528088147b47d7ee5 Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 22:21:43 -0300 Subject: [PATCH 29/30] is: Update ContactInfo test case in organization registry tests --- .../organization_registry_test.go | 26 +++++++++++++++++++ .../storetest/organization_store.go | 6 +++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pkg/identityserver/organization_registry_test.go b/pkg/identityserver/organization_registry_test.go index 7a9cb1f67c..6f5e3e1fa4 100644 --- a/pkg/identityserver/organization_registry_test.go +++ b/pkg/identityserver/organization_registry_test.go @@ -202,6 +202,32 @@ func TestOrganizationsCRUD(t *testing.T) { a.So(errors.IsPermissionDenied(err), should.BeTrue) } + // TODO: Remove (https://github.com/TheThingsNetwork/lorawan-stack/issues/6804) + t.Run("Contact_info fieldmask", func(t *testing.T) { // nolint:paralleltest + a, ctx := test.New(t) + got, err := reg.Get(ctx, &ttnpb.GetOrganizationRequest{ + OrganizationIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, creds) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 2) + a.So(got.ContactInfo[0].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[0].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_OTHER) + a.So(got.ContactInfo[1].Value, should.Equal, usr1.PrimaryEmailAddress) + a.So(got.ContactInfo[1].ContactType, should.Equal, ttnpb.ContactType_CONTACT_TYPE_TECHNICAL) + } + + // Testing the `PublicSafe` method, which should not return the contact_info's email address when the caller + // does not have the appropriate rights. + got, err = reg.Get(ctx, &ttnpb.GetOrganizationRequest{ + OrganizationIds: created.GetIds(), + FieldMask: ttnpb.FieldMask("contact_info"), + }, credsWithoutRights) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got.ContactInfo, should.HaveLength, 0) + } + }) + updated, err := reg.Update(ctx, &ttnpb.UpdateOrganizationRequest{ Organization: &ttnpb.Organization{ Ids: created.GetIds(), diff --git a/pkg/identityserver/storetest/organization_store.go b/pkg/identityserver/storetest/organization_store.go index dd90fb05f8..60b74cc1dc 100644 --- a/pkg/identityserver/storetest/organization_store.go +++ b/pkg/identityserver/storetest/organization_store.go @@ -41,7 +41,7 @@ func (st *StoreTest) TestOrganizationStoreCRUD(t *T) { } defer s.Close() - mask := fieldMask(ttnpb.OrganizationFieldPathsTopLevel...) + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.OrganizationFieldPathsTopLevel...), "contact_info") var created *ttnpb.Organization @@ -325,6 +325,8 @@ func (st *StoreTest) TestOrganizationStorePagination(t *T) { } defer s.Close() + mask := ttnpb.ExcludeFields(fieldMask(ttnpb.OrganizationFieldPathsTopLevel...), "contact_info") + t.Run("FindOrganizations_Paginated", func(t *T) { a, ctx := test.New(t) @@ -332,7 +334,7 @@ func (st *StoreTest) TestOrganizationStorePagination(t *T) { for _, page := range []uint32{1, 2, 3, 4} { paginateCtx := store.WithPagination(ctx, 2, page, &total) - got, err := s.FindOrganizations(paginateCtx, nil, fieldMask(ttnpb.OrganizationFieldPathsTopLevel...)) + got, err := s.FindOrganizations(paginateCtx, nil, mask) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { if page == 4 { a.So(got, should.HaveLength, 1) From 9fd031b6de75333357d84a1fe2c4d32a9b8f716b Mon Sep 17 00:00:00 2001 From: Nicholas Cristofaro Date: Thu, 18 Jan 2024 22:23:34 -0300 Subject: [PATCH 30/30] is: Update contact info registry tests --- .../contact_info_registry_test.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/identityserver/contact_info_registry_test.go b/pkg/identityserver/contact_info_registry_test.go index 466b207b13..a984cf28e4 100644 --- a/pkg/identityserver/contact_info_registry_test.go +++ b/pkg/identityserver/contact_info_registry_test.go @@ -26,33 +26,33 @@ import ( "google.golang.org/grpc" ) -// TODO: This set of tests have to be updated after (https://github.com/TheThingsNetwork/lorawan-stack/issues/6567) is -// resolved. func TestContactInfoValidation(t *testing.T) { t.Parallel() p := &storetest.Population{} usr1 := p.NewUser() - usr1.ContactInfo = []*ttnpb.ContactInfo{ // nolint:staticcheck - { - ContactType: ttnpb.ContactType_CONTACT_TYPE_OTHER, - ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, - Value: "usr@test.com", - Public: false, - ValidatedAt: nil, - }, - } usr1Key, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) usr1Creds := rpcCreds(usr1Key) testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewContactInfoRegistryClient(cc) + a, ctx := test.New(t) retryInterval := test.Delay << 5 is.config.UserRegistration.ContactInfoValidation.Required = true is.config.UserRegistration.ContactInfoValidation.TokenTTL = retryInterval * 2 is.config.UserRegistration.ContactInfoValidation.RetryInterval = retryInterval + // Directly insert contact info into database since entities no longer support creating new contact info. + c, err := is.store.SetContactInfo(ctx, usr1.GetEntityIdentifiers(), []*ttnpb.ContactInfo{{ + ContactType: ttnpb.ContactType_CONTACT_TYPE_OTHER, + ContactMethod: ttnpb.ContactMethod_CONTACT_METHOD_EMAIL, + Value: "usr@test.com", + }}) + a.So(c, should.HaveLength, 1) + a.So(err, should.BeNil) + time.Sleep(3 * retryInterval) // Wait for the validation token to expire. + t.Run("Request Validation", func(t *testing.T) { // nolint:paralleltest t.Run("Insufficient Rights", func(t *testing.T) { // nolint:paralleltest a, ctx := test.New(t)