@@ -125,9 +125,14 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req
|
||||
return fmt.Errorf("cannot update identity: %w", err)
|
||||
}
|
||||
|
||||
profile := &coredata.IdentityProfile{}
|
||||
if err := profile.LoadDefaultByIdentityID(ctx, tx, identityID); err != nil {
|
||||
return fmt.Errorf("cannot load default profile: %w", err)
|
||||
}
|
||||
|
||||
subject, textBody, htmlBody, err := emails.RenderConfirmEmail(
|
||||
s.baseURL,
|
||||
identity.FullName,
|
||||
profile.FullName,
|
||||
confirmationUrl,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -135,7 +140,7 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req
|
||||
}
|
||||
|
||||
confirmationEmail := coredata.NewEmail(
|
||||
identity.FullName,
|
||||
profile.FullName,
|
||||
identity.EmailAddress,
|
||||
subject,
|
||||
textBody,
|
||||
@@ -714,3 +719,157 @@ func (s AccountService) ListOrganizations(ctx context.Context, identityID gid.GI
|
||||
|
||||
return organizations, nil
|
||||
}
|
||||
|
||||
func (s AccountService) GetProfileForMembership(ctx context.Context, membershipID gid.GID) (*coredata.IdentityProfile, error) {
|
||||
var (
|
||||
scope = coredata.NewScopeFromObjectID(membershipID)
|
||||
profile = &coredata.IdentityProfile{}
|
||||
)
|
||||
|
||||
err := s.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
membership := &coredata.Membership{}
|
||||
err := membership.LoadByID(ctx, conn, scope, membershipID)
|
||||
if err != nil {
|
||||
if err == coredata.ErrResourceNotFound {
|
||||
return NewMembershipNotFoundError(membershipID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load membership: %w", err)
|
||||
}
|
||||
|
||||
err = profile.LoadByMembershipID(ctx, conn, scope, membershipID)
|
||||
if err != nil {
|
||||
if err == coredata.ErrResourceNotFound {
|
||||
return NewProfileNotFoundError(membershipID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load identity profile: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
func (s AccountService) GetDefaultProfile(ctx context.Context, identityID gid.GID) (*coredata.IdentityProfile, error) {
|
||||
var profile = &coredata.IdentityProfile{}
|
||||
|
||||
err := s.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := profile.LoadDefaultByIdentityID(ctx, conn, identityID)
|
||||
if err != nil {
|
||||
if err == coredata.ErrResourceNotFound {
|
||||
return NewProfileNotFoundError(identityID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load default profile: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
type UpdateIdentityProfileRequest struct {
|
||||
MembershipID gid.GID
|
||||
FullName *string
|
||||
}
|
||||
|
||||
func (s AccountService) UpdateIdentityProfile(
|
||||
ctx context.Context,
|
||||
identityID gid.GID,
|
||||
req *UpdateIdentityProfileRequest,
|
||||
) (*coredata.IdentityProfile, error) {
|
||||
// var (
|
||||
// scope = coredata.NewScopeFromObjectID(req.MembershipID)
|
||||
// profile = &coredata.IdentityProfile{}
|
||||
// )
|
||||
|
||||
// err := s.pg.WithTx(
|
||||
// ctx,
|
||||
// func(tx pg.Conn) error {
|
||||
// // First verify the membership belongs to the identity
|
||||
// membership := &coredata.Membership{}
|
||||
// err := membership.LoadByID(ctx, tx, scope, req.MembershipID)
|
||||
// if err != nil {
|
||||
// if err == coredata.ErrResourceNotFound {
|
||||
// return NewMembershipNotFoundError(req.MembershipID)
|
||||
// }
|
||||
|
||||
// return fmt.Errorf("cannot load membership: %w", err)
|
||||
// }
|
||||
|
||||
// if membership.IdentityID != identityID {
|
||||
// return NewMembershipNotFoundError(req.MembershipID)
|
||||
// }
|
||||
|
||||
// // Try to load existing membership profile
|
||||
// err = profile.LoadByMembershipID(ctx, tx, scope, req.MembershipID)
|
||||
// if err != nil && err != coredata.ErrResourceNotFound {
|
||||
// return fmt.Errorf("cannot load identity profile: %w", err)
|
||||
// }
|
||||
|
||||
// now := time.Now()
|
||||
|
||||
// if err == coredata.ErrResourceNotFound {
|
||||
// // Create new membership profile, optionally inheriting from default
|
||||
// tenantID := req.MembershipID.TenantID()
|
||||
// membershipID := req.MembershipID
|
||||
|
||||
// // Try to get default profile to inherit FullName
|
||||
// defaultProfile := &coredata.IdentityProfile{}
|
||||
// defaultFullName := ""
|
||||
// if loadErr := defaultProfile.LoadDefaultByIdentityID(ctx, tx, identityID); loadErr == nil {
|
||||
// defaultFullName = defaultProfile.FullName
|
||||
// }
|
||||
|
||||
// tenantIDStr := tenantID.String()
|
||||
// profile = &coredata.IdentityProfile{
|
||||
// ID: gid.New(tenantID, coredata.IdentityProfileEntityType),
|
||||
// TenantID: &tenantIDStr,
|
||||
// IdentityID: identityID,
|
||||
// MembershipID: &membershipID,
|
||||
// FullName: defaultFullName,
|
||||
// CreatedAt: now,
|
||||
// UpdatedAt: now,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Apply updates
|
||||
// if req.FullName != nil {
|
||||
// profile.FullName = *req.FullName
|
||||
// }
|
||||
// profile.UpdatedAt = now
|
||||
|
||||
// // Upsert the membership profile
|
||||
// err = profile.UpsertMembership(ctx, tx)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("cannot upsert identity profile: %w", err)
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// },
|
||||
// )
|
||||
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// return profile, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -158,7 +158,6 @@ func (s *AuthService) CreateIdentityFromInvitation(
|
||||
EmailAddress: invitation.Email,
|
||||
HashedPassword: hashedPassword,
|
||||
EmailAddressVerified: true,
|
||||
FullName: invitation.FullName,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
@@ -172,6 +171,19 @@ func (s *AuthService) CreateIdentityFromInvitation(
|
||||
return fmt.Errorf("cannot insert identity: %w", err)
|
||||
}
|
||||
|
||||
defaultProfile := &coredata.IdentityProfile{
|
||||
ID: gid.New(gid.NilTenant, coredata.IdentityProfileEntityType),
|
||||
IdentityID: identity.ID,
|
||||
FullName: invitation.FullName,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = defaultProfile.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert default profile: %w", err)
|
||||
}
|
||||
|
||||
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, s.sessionDuration)
|
||||
err = session.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
@@ -273,9 +285,14 @@ func (s AuthService) SendPasswordResetInstructionByEmail(
|
||||
return fmt.Errorf("cannot load identity: %w", err)
|
||||
}
|
||||
|
||||
profile := &coredata.IdentityProfile{}
|
||||
if err := profile.LoadDefaultByIdentityID(ctx, tx, identity.ID); err != nil {
|
||||
return fmt.Errorf("cannot load default profile: %w", err)
|
||||
}
|
||||
|
||||
subject, textBody, htmlBody, err := emails.RenderPasswordReset(
|
||||
s.baseURL,
|
||||
identity.FullName,
|
||||
profile.FullName,
|
||||
resetPasswordUrl,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -283,7 +300,7 @@ func (s AuthService) SendPasswordResetInstructionByEmail(
|
||||
}
|
||||
|
||||
passwordResetEmail := coredata.NewEmail(
|
||||
identity.FullName,
|
||||
profile.FullName,
|
||||
identity.EmailAddress,
|
||||
subject,
|
||||
textBody,
|
||||
@@ -325,11 +342,18 @@ func (s AuthService) CreateIdentityWithPassword(
|
||||
EmailAddress: req.Email,
|
||||
HashedPassword: hashedPassword,
|
||||
EmailAddressVerified: false,
|
||||
FullName: req.FullName,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
defaultProfile = &coredata.IdentityProfile{
|
||||
ID: gid.New(gid.NilTenant, coredata.IdentityProfileEntityType),
|
||||
IdentityID: identity.ID,
|
||||
FullName: req.FullName,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
session = coredata.NewRootSession(identity.ID, coredata.AuthMethodPassword, 24*time.Hour*7)
|
||||
)
|
||||
|
||||
@@ -358,7 +382,7 @@ func (s AuthService) CreateIdentityWithPassword(
|
||||
|
||||
subject, textBody, htmlBody, err := emails.RenderConfirmEmail(
|
||||
s.baseURL,
|
||||
identity.FullName,
|
||||
req.FullName,
|
||||
confirmationUrl,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -366,7 +390,7 @@ func (s AuthService) CreateIdentityWithPassword(
|
||||
}
|
||||
|
||||
confirmationEmail := coredata.NewEmail(
|
||||
identity.FullName,
|
||||
req.FullName,
|
||||
identity.EmailAddress,
|
||||
subject,
|
||||
textBody,
|
||||
@@ -385,6 +409,11 @@ func (s AuthService) CreateIdentityWithPassword(
|
||||
return fmt.Errorf("cannot insert identity: %w", err)
|
||||
}
|
||||
|
||||
err = defaultProfile.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert default profile: %w", err)
|
||||
}
|
||||
|
||||
if err := confirmationEmail.Insert(ctx, tx); err != nil {
|
||||
return fmt.Errorf("cannot insert email: %w", err)
|
||||
}
|
||||
|
||||
@@ -200,6 +200,16 @@ func (e ErrPersonalAPIKeyNotFound) Error() string {
|
||||
return fmt.Sprintf("personal API key %q not found", e.PersonalAPIKeyID)
|
||||
}
|
||||
|
||||
type ErrProfileNotFound struct{ MembershipID gid.GID }
|
||||
|
||||
func NewProfileNotFoundError(membershipID gid.GID) error {
|
||||
return &ErrProfileNotFound{MembershipID: membershipID}
|
||||
}
|
||||
|
||||
func (e ErrProfileNotFound) Error() string {
|
||||
return fmt.Sprintf("profile for membership %q not found", e.MembershipID)
|
||||
}
|
||||
|
||||
type ErrPersonalAPIKeyExpired struct{ PersonalAPIKeyID gid.GID }
|
||||
|
||||
func NewPersonalAPIKeyExpiredError(personalAPIKeyID gid.GID) error {
|
||||
|
||||
@@ -260,7 +260,6 @@ func (s *Service) HandleAssertion(
|
||||
EmailAddress: email,
|
||||
HashedPassword: nil,
|
||||
EmailAddressVerified: true,
|
||||
FullName: fullname,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
@@ -269,11 +268,23 @@ func (s *Service) HandleAssertion(
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert identity: %w", err)
|
||||
}
|
||||
|
||||
defaultProfile := &coredata.IdentityProfile{
|
||||
ID: gid.New(gid.NilTenant, coredata.IdentityProfileEntityType),
|
||||
IdentityID: identity.ID,
|
||||
FullName: fullname,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = defaultProfile.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert default profile: %w", err)
|
||||
}
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("cannot load identity: %w", err)
|
||||
} else {
|
||||
identity.SAMLSubject = &assertion.Subject.NameID.Value
|
||||
identity.FullName = fullname
|
||||
identity.EmailAddress = email
|
||||
identity.EmailAddressVerified = true
|
||||
identity.UpdatedAt = now
|
||||
@@ -304,6 +315,20 @@ func (s *Service) HandleAssertion(
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert membership: %w", err)
|
||||
}
|
||||
|
||||
membershipProfile := &coredata.IdentityProfile{
|
||||
ID: gid.New(membership.ID.TenantID(), coredata.IdentityProfileEntityType),
|
||||
IdentityID: identity.ID,
|
||||
MembershipID: &membership.ID,
|
||||
FullName: fullname,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = membershipProfile.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert membership profile: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if role != nil {
|
||||
@@ -316,6 +341,19 @@ func (s *Service) HandleAssertion(
|
||||
}
|
||||
}
|
||||
|
||||
memberProfile := &coredata.IdentityProfile{}
|
||||
err = memberProfile.LoadByMembershipID(ctx, tx, coredata.NewNoScope(), membership.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load membership profile: %w", err)
|
||||
}
|
||||
|
||||
memberProfile.FullName = fullname
|
||||
memberProfile.UpdatedAt = now
|
||||
err = memberProfile.Update(ctx, tx, coredata.NewNoScope())
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot update membership profile: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user