From c5a7d39a7ee4a464d27bd88c84ed3b4a96f16e24 Mon Sep 17 00:00:00 2001 From: Daniel Sollis Date: Thu, 11 May 2023 00:10:55 -0700 Subject: [PATCH] sc-14491 Part 2: Updates GDS endpoints (#1008) * initial work * changes based on PR review * fixed comment * updated GDS endpoints * tests working * more PR review based changes * more PR review changes * fixed build issue * fixed other build issue * fixed import * fixed lint * more PR review changes * updated documentation * Update pkg/gds/gds.go Co-authored-by: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> * Update pkg/gds/gds.go Co-authored-by: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> * Update pkg/gds/gds.go Co-authored-by: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> * Update pkg/gds/gds.go Co-authored-by: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> * Update pkg/gds/gds.go Co-authored-by: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> * Update pkg/gds/gds.go Co-authored-by: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> * pr review changes --------- Co-authored-by: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> --- pkg/bff/models/v1/models.pb.go | 4 +- pkg/gds/emails/client.go | 29 +++++++ pkg/gds/gds.go | 137 ++++++++++++++++++++++--------- pkg/gds/gds_test.go | 39 +++------ pkg/models/v1/models.go | 22 +++++ pkg/models/v1/models.pb.go | 115 +++++++++++++++----------- proto/gds/models/v1/models.proto | 16 ++-- 7 files changed, 244 insertions(+), 118 deletions(-) diff --git a/pkg/bff/models/v1/models.pb.go b/pkg/bff/models/v1/models.pb.go index 423b1bf8c..647a404ef 100644 --- a/pkg/bff/models/v1/models.pb.go +++ b/pkg/bff/models/v1/models.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc-gen-go v1.28.0 +// protoc v3.6.1 // source: bff/models/v1/models.proto package models diff --git a/pkg/gds/emails/client.go b/pkg/gds/emails/client.go index 2a1656a33..c1cb8f7dd 100644 --- a/pkg/gds/emails/client.go +++ b/pkg/gds/emails/client.go @@ -151,6 +151,35 @@ func (m *EmailManager) SendVerifyContact(vasp *pb.VASP, contact *pb.Contact) (er return nil } +// SendVerifyContact sends a verification email to a contact. +func (m *EmailManager) SendVerifyModelContact(vasp *pb.VASP, contact *models.Contact) (err error) { + ctx := VerifyContactData{ + Name: contact.Name, + VID: vasp.Id, + BaseURL: m.conf.VerifyContactBaseURL, + DirectoryID: m.conf.DirectoryID, + } + + ctx.Token = contact.Token + + msg, err := VerifyContactEmail( + m.serviceEmail.Name, m.serviceEmail.Address, + contact.Name, contact.Email, + ctx, + ) + if err != nil { + log.Error().Err(err).Msg("could not create verify contact email") + return err + } + + if err = m.Send(msg); err != nil { + log.Error().Err(err).Msg("could not send verify contact email") + return err + } + contact.AppendEmailLog(string(admin.ResendVerifyContact), msg.Subject) + return nil +} + // SendReviewRequest is a shortcut for iComply verification in which we simply send // an email to the TRISA admins and have them manually verify registrations. func (m *EmailManager) SendReviewRequest(vasp *pb.VASP) (sent int, err error) { diff --git a/pkg/gds/gds.go b/pkg/gds/gds.go index 2cadcaa43..6c3d57567 100644 --- a/pkg/gds/gds.go +++ b/pkg/gds/gds.go @@ -13,10 +13,12 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/trisacrypto/directory/pkg" + admin "github.com/trisacrypto/directory/pkg/gds/admin/v2" "github.com/trisacrypto/directory/pkg/gds/config" "github.com/trisacrypto/directory/pkg/gds/secrets" "github.com/trisacrypto/directory/pkg/models/v1" "github.com/trisacrypto/directory/pkg/store" + storeerrors "github.com/trisacrypto/directory/pkg/store/errors" "github.com/trisacrypto/directory/pkg/utils" api "github.com/trisacrypto/trisa/pkg/trisa/gds/api/v1beta1" pb "github.com/trisacrypto/trisa/pkg/trisa/gds/models/v1beta1" @@ -174,7 +176,7 @@ func (s *GDS) Register(ctx context.Context, in *api.RegisterRequest) (out *api.R // Retrieve email address from one of the supplied contacts. var email string - if email = getContactEmail(vasp); email == "" { + if email = GetContactEmail(vasp); email == "" { log.Error().Err(errors.New("no contact email address found")).Msg("incorrect access on validated VASP") return nil, status.Error(codes.InvalidArgument, "no email address in supplied VASP contacts") } @@ -196,39 +198,74 @@ func (s *GDS) Register(ctx context.Context, in *api.RegisterRequest) (out *api.R } // Log successful registration - name, _ := vasp.Name() - log.Info().Str("name", name).Str("id", vasp.Id).Msg("registered VASP") + vaspName, _ := vasp.Name() + log.Info().Str("name", vaspName).Str("id", vasp.Id).Msg("registered VASP") // Begin verification process by sending emails to all contacts in the VASP record. // TODO: add to processing queue to return sooner/parallelize work // Create the verification tokens and save the VASP back to the database + sent := 0 iter := models.NewContactIterator(vasp.Contacts, true, false) for iter.Next() { - contact, kind := iter.Value() - if err = models.SetContactVerification(contact, secrets.CreateToken(48), false); err != nil { + vaspContact, kind := iter.Value() + + // Add the secret token to the vasp contact + token := secrets.CreateToken(48) + if err = models.SetContactVerification(vaspContact, token, false); err != nil { log.Error().Err(err).Str("contact", kind).Str("vasp", vasp.Id).Msg("could not set contact verification token") return nil, status.Error(codes.Aborted, "could not send contact verification emails") } - } - - if err = s.db.UpdateVASP(ctx, vasp); err != nil { - log.Error().Err(err).Str("vasp", vasp.Id).Msg("could not update vasp with contact verification tokens") - return nil, status.Error(codes.Aborted, "could not send contact verification emails") - } - // Send contacts with updated tokens - var sent int - if sent, err = s.svc.email.SendVerifyContacts(vasp); err != nil { - // If there is an error sending contact verification emails, alert admins who - // can resend emails later, do not abort processing the registration. - log.Error().Err(err).Str("vasp", vasp.Id).Int("sent", sent).Msg("could not send verify contacts emails") - } else { - // Log successful contact verification emails sent - log.Info().Int("sent", sent).Msg("contact email verifications sent") + // If there does not exist a model contact associated with the vasp contact's email then create one. + var contact *models.Contact + contact, err = s.db.RetrieveContact(ctx, vaspContact.Email) + if err != nil { + if errors.Is(err, storeerrors.ErrEntityNotFound) { + contact = &models.Contact{ + Email: vaspContact.Email, + Name: vaspContact.Name, + Vasps: []string{vasp.CommonName}, + Token: token, + } + if _, err = s.db.CreateContact(ctx, contact); err != nil { + log.Error().Err(err).Str("contact", vaspContact.Email).Str("vasp", vasp.Id).Msg("could not create contact") + return nil, status.Error(codes.Aborted, "could not create contact") + } + } else { + log.Warn().Err(err).Msg("could not register contact in database") + return nil, status.Error(codes.AlreadyExists, "could not complete registration") + } + } - if err = s.db.UpdateVASP(ctx, vasp); err != nil { - log.Error().Err(err).Str("vasp", vasp.Id).Msg("could not update email logs on vasp") - return nil, status.Error(codes.Aborted, "could not update vasp record") + if !contact.Verified { + // Begin verification process by sending email to the contact created from the VASP record. + // TODO: add to processing queue to return sooner/parallelize work + if err = s.svc.email.SendVerifyModelContact(vasp, contact); err != nil { + // If there is an error sending contact verification emails, alert admins who + // can resend emails later, do not abort processing the registration. + log.Error().Err(err).Str("vasp", vasp.Id).Int("sent", sent).Msg("could not send verify contacts emails") + } else { + // Log successful contact verification emails sent + log.Info().Msg("contact email verification sent") + sent++ + + // Update the model contact record to update the email log + if err = s.db.UpdateContact(ctx, contact); err != nil { + log.Error().Err(err).Str("contact", contact.Email).Msg("could not update email logs on contact") + return nil, status.Error(codes.Aborted, "could not send contact verification emails") + } + models.AppendEmailLog(vaspContact, string(admin.ResendVerifyContact), "verify_contact") + } + } else { + // If the model contact is verified make sure that the vasp contact is verified as well + if err = models.SetContactVerification(vaspContact, token, true); err != nil { + log.Error().Err(err).Str("contact", kind).Str("vasp", vasp.Id).Msg("could not set contact verification token") + return nil, status.Error(codes.Aborted, "could not send contact verification emails") + } + if err = s.db.UpdateVASP(ctx, vasp); err != nil { + log.Error().Err(err).Str("vasp", vasp.Id).Msg("could not update contact verification on vasp") + return nil, status.Error(codes.Internal, "internal error with registration, please contact admins") + } } } @@ -279,7 +316,7 @@ func (s *GDS) Register(ctx context.Context, in *api.RegisterRequest) (out *api.R RegisteredDirectory: vasp.RegisteredDirectory, CommonName: vasp.CommonName, Status: vasp.VerificationStatus, - Message: "a verification code has been sent to contact emails, please check spam folder if it has not arrived; pkcs12 password attached, this is the only time it will be available -- do not lose!", + Message: "a verification code has been sent to the contact email, please check spam folder if it has not arrived; pkcs12 password attached, this is the only time it will be available -- do not lose!", Pkcs12Password: password, } return out, nil @@ -470,40 +507,64 @@ func (s *GDS) VerifyContact(ctx context.Context, in *api.VerifyContactRequest) ( return nil, status.Error(codes.NotFound, "could not find associated VASP record by ID") } - // Search through the contacts to determine the contacts verified by the supplied token. - prevVerified := 0 found := false + prevVerified := 0 contactEmail := "" + // Search through the contacts to determine the contacts verified by the supplied token. iter := models.NewContactIterator(vasp.Contacts, false, false) for iter.Next() { - contact, kind := iter.Value() + vaspContact, kind := iter.Value() + var contact *models.Contact + if contact, err = s.db.RetrieveContact(ctx, vaspContact.Email); err != nil { + log.Warn().Err(err).Str("contact", contactEmail).Msg("could not retrieve contact") + return nil, status.Error(codes.NotFound, "could not find associated contact record by email") + } + // Get the verification status - token, verified, err := models.GetContactVerification(contact) + var token string + var verified bool + token, verified, err = models.GetContactVerification(vaspContact) if err != nil { log.Error().Err(err).Msg("could not retrieve verification from contact extra data field") return nil, status.Error(codes.Aborted, "could not verify contact") } + // If the model contact is verified make sure the vasp contact is verified + if contact.Verified { + found = true + prevVerified++ + if err = models.SetContactVerification(vaspContact, token, true); err != nil { + log.Error().Err(err).Str("contact", kind).Str("vasp", vasp.Id).Msg("could not set contact verification token") + return nil, status.Error(codes.Aborted, "could not verify contact") + } + continue + } + // Perform token check and if token matches, mark contact as verified if token == in.Token { found = true - log.Info().Str("vasp", vasp.Id).Str("contact", kind).Msg("contact email verified") - if err = models.SetContactVerification(contact, "", true); err != nil { - log.Error().Err(err).Msg("could not set verification on contact extra data field") + + // Verify and update the models contact + contact.Verified = true + if err = s.db.UpdateContact(ctx, contact); err != nil { + log.Error().Err(err).Str("contact", contact.Email).Msg("could not update email logs on contact") + return nil, status.Error(codes.Aborted, "could not update contact record") + } + + // Verify the vasp contact + if err = models.SetContactVerification(vaspContact, token, true); err != nil { + log.Error().Err(err).Str("contact", kind).Str("vasp", vasp.Id).Msg("could not set contact verification token") return nil, status.Error(codes.Aborted, "could not verify contact") } - contactEmail = contact.Email // Record the contact as verified in the audit log - if err := models.UpdateVerificationStatus(vasp, vasp.VerificationStatus, "contact verified", contactEmail); err != nil { + if err := models.UpdateVerificationStatus(vasp, vasp.VerificationStatus, "contact verified", contact.Email); err != nil { log.Warn().Err(err).Msg("could not append contact verification to VASP audit log") return nil, status.Error(codes.Aborted, "could not add new entry to VASP audit log") } } else if verified { - // Determine the total number of contacts previously verified, not including - // the current contact that was just verified. This will help prevent - // sending multiple emails to the TRISA Admins for review. + found = true prevVerified++ } } @@ -608,8 +669,8 @@ func (s *GDS) Status(ctx context.Context, in *api.HealthCheck) (out *api.Service // Helper Functions //=========================================================================== -// Get a valid email address from the contacts on a VASP. -func getContactEmail(vasp *pb.VASP) string { +// Get a valid email address and name from the contacts on a VASP. +func GetContactEmail(vasp *pb.VASP) string { iter := models.NewContactIterator(vasp.Contacts, true, false) for iter.Next() { contact, _ := iter.Value() diff --git a/pkg/gds/gds_test.go b/pkg/gds/gds_test.go index 8a465d462..de9a60710 100644 --- a/pkg/gds/gds_test.go +++ b/pkg/gds/gds_test.go @@ -504,6 +504,15 @@ func (s *gdsTestSuite) TestVerifyContact() { _, err = client.VerifyContact(ctx, request) require.Error(err) + iter := models.NewContactIterator(charlie.Contacts, false, false) + for iter.Next() { + contact, _ := iter.Value() + s.svc.GetStore().CreateContact(ctx, &models.Contact{ + Email: contact.Email, + Token: "administrative_token", + }) + } + // Successful verification request.Token = "administrative_token" sent := time.Now() @@ -519,35 +528,16 @@ func (s *gdsTestSuite) TestVerifyContact() { token, err := models.GetAdminVerificationToken(vasp) require.NoError(err) require.NotEmpty(token) - token, verified, err := models.GetContactVerification(vasp.Contacts.Administrative) - require.NoError(err) - require.Empty(token) - require.True(verified) - // Verify a different contact - request.Token = "legal_token" - reply, err = client.VerifyContact(ctx, request) - require.NoError(err) - require.Nil(reply.Error) - require.Equal(pb.VerificationState_PENDING_REVIEW, reply.Status) - // Should only change the fields on the contact - vasp, err = s.svc.GetStore().RetrieveVASP(context.Background(), request.Id) - require.NoError(err) - require.Equal(pb.VerificationState_PENDING_REVIEW, vasp.VerificationStatus) - token, verified, err = models.GetContactVerification(vasp.Contacts.Legal) - require.NoError(err) - require.Empty(token) - require.True(verified) - - // Attempt to verify an already verified contact - should fail - request.Token = "legal_token" + // Attempt to verify an already verified contact + request.Token = "administrative_token" _, err = client.VerifyContact(ctx, request) - require.Error(err) + require.NoError(err) // Check audit log entries log, err := models.GetAuditLog(vasp) require.NoError(err) - require.Len(log, 5) + require.Len(log, 4) // Pre-existing entry for SUBMITTED require.Equal(pb.VerificationState_SUBMITTED, log[0].CurrentState) // Administrative contact verified @@ -556,9 +546,6 @@ func (s *gdsTestSuite) TestVerifyContact() { // State of the VASP changes to EMAIL_VERIFIED then PENDING_REVIEW require.Equal(pb.VerificationState_EMAIL_VERIFIED, log[2].CurrentState) require.Equal(pb.VerificationState_PENDING_REVIEW, log[3].CurrentState) - // Legal contact verified - require.Equal(pb.VerificationState_PENDING_REVIEW, log[4].CurrentState) - require.Equal(vasp.Contacts.Legal.Email, log[4].Source) // Only one email should be sent to the admins messages := []*emails.EmailMeta{ diff --git a/pkg/models/v1/models.go b/pkg/models/v1/models.go index 1131162de..0b507c324 100644 --- a/pkg/models/v1/models.go +++ b/pkg/models/v1/models.go @@ -222,6 +222,28 @@ func GetVASPEmailLog(vasp *pb.VASP) (emails []*EmailLogEntry, err error) { return emails, nil } +// Create and add a new entry to the EmailLog on the extra data on the Contact record. +func (c *Contact) AppendEmailLog(reason, subject string) { + // Contact must be non-nil. + if c == nil { + return + } + + // Create the EmailLog if it is nil. + if c.EmailLog == nil { + c.EmailLog = make([]*EmailLogEntry, 0, 1) + } + + // Append entry to the previous log. + entry := &EmailLogEntry{ + Timestamp: time.Now().Format(time.RFC3339), + Reason: reason, + Subject: subject, + Recipient: c.Email, + } + c.EmailLog = append(c.EmailLog, entry) +} + // Normalize the email and convert to bytes func NormalizeEmail(email string) string { trimmed := strings.TrimSpace(email) diff --git a/pkg/models/v1/models.pb.go b/pkg/models/v1/models.pb.go index 81c6ad263..d1498d27a 100644 --- a/pkg/models/v1/models.pb.go +++ b/pkg/models/v1/models.pb.go @@ -925,16 +925,19 @@ type Contact struct { // Unique email address for this contact Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // used to address the email // List of VASPs that the contact is included in - Vasps []string `protobuf:"bytes,2,rep,name=vasps,proto3" json:"vasps,omitempty"` + Vasps []string `protobuf:"bytes,3,rep,name=vasps,proto3" json:"vasps,omitempty"` // Token for email verification - Verified bool `protobuf:"varint,3,opt,name=verified,proto3" json:"verified,omitempty"` - Token string `protobuf:"bytes,4,opt,name=token,proto3" json:"token,omitempty"` + Verified bool `protobuf:"varint,4,opt,name=verified,proto3" json:"verified,omitempty"` + Token string `protobuf:"bytes,5,opt,name=token,proto3" json:"token,omitempty"` + // + EmailLog []*EmailLogEntry `protobuf:"bytes,6,rep,name=email_log,json=emailLog,proto3" json:"email_log,omitempty"` // RFC3339 Timestamp for when the contact was verified - VerifiedOn string `protobuf:"bytes,5,opt,name=verified_on,json=verifiedOn,proto3" json:"verified_on,omitempty"` + VerifiedOn string `protobuf:"bytes,7,opt,name=verified_on,json=verifiedOn,proto3" json:"verified_on,omitempty"` // Logging information timestamps - Created string `protobuf:"bytes,6,opt,name=created,proto3" json:"created,omitempty"` - Modified string `protobuf:"bytes,7,opt,name=modified,proto3" json:"modified,omitempty"` + Created string `protobuf:"bytes,8,opt,name=created,proto3" json:"created,omitempty"` + Modified string `protobuf:"bytes,9,opt,name=modified,proto3" json:"modified,omitempty"` } func (x *Contact) Reset() { @@ -976,6 +979,13 @@ func (x *Contact) GetEmail() string { return "" } +func (x *Contact) GetName() string { + if x != nil { + return x.Name + } + return "" +} + func (x *Contact) GetVasps() []string { if x != nil { return x.Vasps @@ -997,6 +1007,13 @@ func (x *Contact) GetToken() string { return "" } +func (x *Contact) GetEmailLog() []*EmailLogEntry { + if x != nil { + return x.EmailLog + } + return nil +} + func (x *Contact) GetVerifiedOn() string { if x != nil { return x.VerifiedOn @@ -1236,41 +1253,46 @@ var file_gds_models_v1_models_proto_rawDesc = []byte{ 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, - 0x65, 0x6e, 0x74, 0x22, 0xbe, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, + 0x65, 0x6e, 0x74, 0x22, 0x8d, 0x02, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x73, 0x70, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x73, 0x70, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, - 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x76, - 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1f, 0x0a, - 0x0b, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x69, - 0x66, 0x69, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x69, - 0x66, 0x69, 0x65, 0x64, 0x22, 0x46, 0x0a, 0x0a, 0x50, 0x61, 0x67, 0x65, 0x43, 0x75, 0x72, 0x73, - 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x76, 0x61, 0x73, 0x70, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x78, 0x74, 0x56, 0x61, 0x73, 0x70, 0x2a, 0x38, 0x0a, 0x10, - 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x53, 0x53, 0x55, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, - 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x56, - 0x4f, 0x4b, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xa0, 0x01, 0x0a, 0x17, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x54, 0x4f, 0x5f, - 0x53, 0x55, 0x42, 0x4d, 0x49, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, 0x43, - 0x45, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x4f, 0x57, 0x4e, - 0x4c, 0x4f, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x4f, 0x57, - 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, - 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x52, 0x5f, 0x52, - 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x52, 0x5f, - 0x45, 0x52, 0x52, 0x4f, 0x52, 0x45, 0x44, 0x10, 0x07, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x69, 0x73, 0x61, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x2f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x6f, 0x64, 0x65, - 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x73, + 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x73, 0x70, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x39, 0x0a, 0x09, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x64, 0x73, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x08, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x4c, 0x6f, 0x67, 0x12, 0x1f, 0x0a, 0x0b, + 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x69, 0x66, + 0x69, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x64, 0x69, 0x66, + 0x69, 0x65, 0x64, 0x22, 0x46, 0x0a, 0x0a, 0x50, 0x61, 0x67, 0x65, 0x43, 0x75, 0x72, 0x73, 0x6f, + 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x76, 0x61, 0x73, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6e, 0x65, 0x78, 0x74, 0x56, 0x61, 0x73, 0x70, 0x2a, 0x38, 0x0a, 0x10, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x0a, 0x0a, 0x06, 0x49, 0x53, 0x53, 0x55, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x45, + 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x56, 0x4f, + 0x4b, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xa0, 0x01, 0x0a, 0x17, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x54, 0x4f, 0x5f, 0x53, + 0x55, 0x42, 0x4d, 0x49, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, 0x43, 0x45, + 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x4f, 0x57, 0x4e, 0x4c, + 0x4f, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x4f, 0x57, 0x4e, + 0x4c, 0x4f, 0x41, 0x44, 0x45, 0x44, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, + 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x52, 0x5f, 0x52, 0x45, + 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x52, 0x5f, 0x45, + 0x52, 0x52, 0x4f, 0x52, 0x45, 0x44, 0x10, 0x07, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x72, 0x69, 0x73, 0x61, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x2f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1319,12 +1341,13 @@ var file_gds_models_v1_models_proto_depIdxs = []int32{ 15, // 10: gds.models.v1.AuditLogEntry.previous_state:type_name -> trisa.gds.models.v1beta1.VerificationState 15, // 11: gds.models.v1.AuditLogEntry.current_state:type_name -> trisa.gds.models.v1beta1.VerificationState 9, // 12: gds.models.v1.GDSContactExtraData.email_log:type_name -> gds.models.v1.EmailLogEntry - 7, // 13: gds.models.v1.GDSExtraData.ReviewNotesEntry.value:type_name -> gds.models.v1.ReviewNote - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 9, // 13: gds.models.v1.Contact.email_log:type_name -> gds.models.v1.EmailLogEntry + 7, // 14: gds.models.v1.GDSExtraData.ReviewNotesEntry.value:type_name -> gds.models.v1.ReviewNote + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_gds_models_v1_models_proto_init() } diff --git a/proto/gds/models/v1/models.proto b/proto/gds/models/v1/models.proto index 4fa34a30b..3b6ad515d 100644 --- a/proto/gds/models/v1/models.proto +++ b/proto/gds/models/v1/models.proto @@ -188,20 +188,24 @@ message EmailLogEntry { message Contact { // Unique email address for this contact string email = 1; + string name = 2; // used to address the email // List of VASPs that the contact is included in - repeated string vasps = 2; + repeated string vasps = 3; // Token for email verification - bool verified = 3; - string token = 4; + bool verified = 4; + string token = 5; + + // Email audit log + repeated EmailLogEntry email_log = 6; // RFC3339 Timestamp for when the contact was verified - string verified_on = 5; + string verified_on = 7; // Logging information timestamps - string created = 6; - string modified = 7; + string created = 8; + string modified = 9; } // Implements a protocol buffer struct for state managed pagination. This struct will be