mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
chore: send email on role update (#8489)
* chore: send email on role update * fix: minor changes * fix: update template * fix: minor changes * fix: return updated user
This commit is contained in:
parent
497315579f
commit
552d44d208
@ -289,43 +289,6 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := h.module.GetUserByID(ctx, claims.OrgID, id)
|
|
||||||
if err != nil {
|
|
||||||
render.Error(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// only displayName, role can be updated
|
|
||||||
if user.DisplayName == "" {
|
|
||||||
user.DisplayName = existingUser.DisplayName
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.Role == "" {
|
|
||||||
user.Role = existingUser.Role
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.Role != existingUser.Role && claims.Role != types.RoleAdmin {
|
|
||||||
render.Error(w, errors.New(errors.TypeForbidden, errors.CodeForbidden, "only admins can change roles"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the request is not demoting the last admin user.
|
|
||||||
// also an admin user can only change role of their own or other user
|
|
||||||
if user.Role != existingUser.Role && existingUser.Role == types.RoleAdmin.String() {
|
|
||||||
adminUsers, err := h.module.GetUsersByRoleInOrg(ctx, claims.OrgID, types.RoleAdmin)
|
|
||||||
if err != nil {
|
|
||||||
render.Error(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(adminUsers) == 1 {
|
|
||||||
render.Error(w, errors.New(errors.TypeForbidden, errors.CodeForbidden, "cannot demote the last admin"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user.UpdatedAt = time.Now()
|
|
||||||
|
|
||||||
updatedUser, err := h.module.UpdateUser(ctx, claims.OrgID, id, &user, claims.UserID)
|
updatedUser, err := h.module.UpdateUser(ctx, claims.OrgID, id, &user, claims.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(w, err)
|
render.Error(w, err)
|
||||||
|
|||||||
@ -176,18 +176,69 @@ func (m *Module) ListUsers(ctx context.Context, orgID string) ([]*types.Gettable
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) UpdateUser(ctx context.Context, orgID string, id string, user *types.User, updatedBy string) (*types.User, error) {
|
func (m *Module) UpdateUser(ctx context.Context, orgID string, id string, user *types.User, updatedBy string) (*types.User, error) {
|
||||||
user, err := m.store.UpdateUser(ctx, orgID, id, user)
|
|
||||||
|
existingUser, err := m.GetUserByID(ctx, orgID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
traits := types.NewTraitsFromUser(user)
|
requestor, err := m.GetUserByID(ctx, orgID, updatedBy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// only displayName, role can be updated
|
||||||
|
if user.DisplayName == "" {
|
||||||
|
user.DisplayName = existingUser.DisplayName
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Role == "" {
|
||||||
|
user.Role = existingUser.Role
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Role != existingUser.Role && requestor.Role != types.RoleAdmin.String() {
|
||||||
|
return nil, errors.New(errors.TypeForbidden, errors.CodeForbidden, "only admins can change roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the request is not demoting the last admin user.
|
||||||
|
// also an admin user can only change role of their own or other user
|
||||||
|
if user.Role != existingUser.Role && existingUser.Role == types.RoleAdmin.String() {
|
||||||
|
adminUsers, err := m.GetUsersByRoleInOrg(ctx, orgID, types.RoleAdmin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(adminUsers) == 1 {
|
||||||
|
return nil, errors.New(errors.TypeForbidden, errors.CodeForbidden, "cannot demote the last admin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user.UpdatedAt = time.Now()
|
||||||
|
|
||||||
|
updatedUser, err := m.store.UpdateUser(ctx, orgID, id, user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
traits := types.NewTraitsFromUser(updatedUser)
|
||||||
m.analytics.IdentifyUser(ctx, user.OrgID, user.ID.String(), traits)
|
m.analytics.IdentifyUser(ctx, user.OrgID, user.ID.String(), traits)
|
||||||
|
|
||||||
traits["updated_by"] = updatedBy
|
traits["updated_by"] = updatedBy
|
||||||
m.analytics.TrackUser(ctx, user.OrgID, user.ID.String(), "User Updated", traits)
|
m.analytics.TrackUser(ctx, user.OrgID, user.ID.String(), "User Updated", traits)
|
||||||
|
|
||||||
return user, nil
|
// if the role is updated then send an email
|
||||||
|
if existingUser.Role != updatedUser.Role {
|
||||||
|
if err := m.emailing.SendHTML(ctx, existingUser.Email, "Your Role is updated in SigNoz", emailtypes.TemplateNameUpdateRole, map[string]any{
|
||||||
|
"CustomerName": existingUser.DisplayName,
|
||||||
|
"UpdatedByEmail": requestor.Email,
|
||||||
|
"OldRole": existingUser.Role,
|
||||||
|
"NewRole": updatedUser.Role,
|
||||||
|
}); err != nil {
|
||||||
|
m.settings.Logger().ErrorContext(ctx, "failed to send email", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedUser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) DeleteUser(ctx context.Context, orgID string, id string, deletedBy string) error {
|
func (m *Module) DeleteUser(ctx context.Context, orgID string, id string, deletedBy string) error {
|
||||||
|
|||||||
@ -12,11 +12,12 @@ import (
|
|||||||
var (
|
var (
|
||||||
// Templates is a list of all the templates that are supported by the emailing service.
|
// Templates is a list of all the templates that are supported by the emailing service.
|
||||||
// This list should be updated whenever a new template is added.
|
// This list should be updated whenever a new template is added.
|
||||||
Templates = []TemplateName{TemplateNameInvitationEmail}
|
Templates = []TemplateName{TemplateNameInvitationEmail, TemplateNameUpdateRole}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TemplateNameInvitationEmail = TemplateName{valuer.NewString("invitation_email")}
|
TemplateNameInvitationEmail = TemplateName{valuer.NewString("invitation_email")}
|
||||||
|
TemplateNameUpdateRole = TemplateName{valuer.NewString("update_role")}
|
||||||
)
|
)
|
||||||
|
|
||||||
type TemplateName struct{ valuer.String }
|
type TemplateName struct{ valuer.String }
|
||||||
@ -25,6 +26,8 @@ func NewTemplateName(name string) (TemplateName, error) {
|
|||||||
switch name {
|
switch name {
|
||||||
case TemplateNameInvitationEmail.StringValue():
|
case TemplateNameInvitationEmail.StringValue():
|
||||||
return TemplateNameInvitationEmail, nil
|
return TemplateNameInvitationEmail, nil
|
||||||
|
case TemplateNameUpdateRole.StringValue():
|
||||||
|
return TemplateNameUpdateRole, nil
|
||||||
default:
|
default:
|
||||||
return TemplateName{}, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid template name: %s", name)
|
return TemplateName{}, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid template name: %s", name)
|
||||||
}
|
}
|
||||||
|
|||||||
23
templates/email/update_role.gotmpl
Normal file
23
templates/email/update_role.gotmpl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>Hi {{.CustomerName}},</p>
|
||||||
|
|
||||||
|
<p>We wanted to inform you that your role in the <strong>SigNoz</strong> project has been updated by <strong>{{.UpdatedByEmail}}</strong>.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Previous Role:</strong> {{.OldRole}}<br>
|
||||||
|
<strong>New Role:</strong> {{.NewRole}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Please note that you will need to <strong>log out and log back in</strong> for the changes to take effect.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you were not expecting this change or have any questions, please reach out to your project administrator or contact us at <a href="mailto:support@signoz.io">support@signoz.io</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Thanks,<br/>The SigNoz Team</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user