mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-27 05:40:40 +01:00
#### Description
- Removes the deprecated user endpoints that now have v2 replacements:
- `POST /api/v1/invite`, `GET /api/v1/user`, `GET
/api/v1/getResetPasswordToken/{id}`, `POST /api/v1/resetPassword`
- `POST /api/v2/users/{id}/roles` and `DELETE
/api/v2/users/{id}/roles/{roleId}`, superseded by `/api/v2/user_roles`
- `GET /api/v1/user/me` stays registered but returns 501 pointing at
`GET /api/v2/users/me`, following the v1 dashboard endpoints.
- Drops the handlers, module methods and types that only existed to
serve them (`DeprecatedUser`, the `user_invite` types, `PostableRole`).
- Moves the four remaining `*_cleanup` teardown tests off `DELETE
/api/v2/users/{id}/roles/{roleId}` onto `DELETE
/api/v2/user_roles/{id}`. Removal is keyed by the `user_role` entry id,
so they read it from `GET /api/v2/users/{id}` — that endpoint is not
deprecated and its other uses are untouched.
- Regenerates `docs/api/openapi.yml` and the frontend client. No
hand-written frontend code referenced the removed operations.
#### Issues closed by this PR
Closes: https://github.com/SigNoz/platform-pod/issues/2667
#### Additional Information
- `/api/v1/user/me` is a stub rather than a deletion because an
unregistered `/api/*` path falls through to the SPA catch-all and
answers 200 with `index.html`, which older mcp reads as a successful
response — a 501 fails loudly instead.
161 lines
5.3 KiB
Go
161 lines
5.3 KiB
Go
package impluser
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
|
|
"github.com/SigNoz/signoz/pkg/errors"
|
|
"github.com/SigNoz/signoz/pkg/flagger"
|
|
"github.com/SigNoz/signoz/pkg/modules/user"
|
|
"github.com/SigNoz/signoz/pkg/types"
|
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
|
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
|
"github.com/SigNoz/signoz/pkg/valuer"
|
|
)
|
|
|
|
type getter struct {
|
|
store types.UserStore
|
|
userRoleStore authtypes.UserRoleStore
|
|
flagger flagger.Flagger
|
|
}
|
|
|
|
func NewGetter(store types.UserStore, userRoleStore authtypes.UserRoleStore, flagger flagger.Flagger) user.Getter {
|
|
return &getter{store: store, userRoleStore: userRoleStore, flagger: flagger}
|
|
}
|
|
|
|
func (module *getter) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID) (*types.User, []*authtypes.UserRole, error) {
|
|
rootUser, err := module.store.GetRootUserByOrgID(ctx, orgID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
userRoles, err := module.userRoleStore.GetUserRolesByUserID(ctx, rootUser.ID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return rootUser, userRoles, nil
|
|
}
|
|
|
|
func (module *getter) ListUsersByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.User, error) {
|
|
users, err := module.store.ListUsersByOrgID(ctx, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// filter root users if feature flag `hide_root_users` is true
|
|
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
|
|
hideRootUsers := module.flagger.BooleanOrEmpty(ctx, flagger.FeatureHideRootUser, evalCtx)
|
|
|
|
if hideRootUsers {
|
|
users = slices.DeleteFunc(users, func(user *types.User) bool { return user.IsRoot })
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
func (module *getter) GetUserByOrgIDAndID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*types.User, error) {
|
|
return module.store.GetByOrgIDAndID(ctx, orgID, userID)
|
|
}
|
|
|
|
func (module *getter) ListUsersByEmailAndOrgIDs(ctx context.Context, email valuer.Email, orgIDs []valuer.UUID) ([]*types.User, error) {
|
|
return module.store.ListUsersByEmailAndOrgIDs(ctx, email, orgIDs)
|
|
}
|
|
|
|
func (module *getter) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) {
|
|
count, err := module.store.CountByOrgID(ctx, orgID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
func (module *getter) CountByOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, statuses []string) (map[valuer.String]int64, error) {
|
|
counts, err := module.store.CountByOrgIDAndStatuses(ctx, orgID, statuses)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return counts, nil
|
|
}
|
|
|
|
func (module *getter) GetFactorPasswordByUserID(ctx context.Context, userID valuer.UUID) (*types.FactorPassword, error) {
|
|
factorPassword, err := module.store.GetPasswordByUserID(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return factorPassword, nil
|
|
}
|
|
|
|
// GetNonDeletedUserByEmailAndOrgID restricts that only one non-deleted user email can exist for an org ID, if found more, it throws an error.
|
|
func (module *getter) GetNonDeletedUserByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) (*types.User, error) {
|
|
existingUsers, err := module.store.GetNonDeletedUsersByEmailAndOrgID(ctx, email, orgID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(existingUsers) > 1 {
|
|
return nil, errors.Newf(errors.TypeInternal, errors.CodeInternal, "Multiple non-deleted users found for email %s in org_id: %s", email.StringValue(), orgID.StringValue())
|
|
}
|
|
|
|
if len(existingUsers) == 1 {
|
|
return existingUsers[0], nil
|
|
}
|
|
|
|
return nil, errors.Newf(errors.TypeNotFound, errors.CodeNotFound, "No non-deleted user found with email %s in org_id: %s", email.StringValue(), orgID.StringValue())
|
|
|
|
}
|
|
|
|
func (module *getter) GetRolesByUserID(ctx context.Context, userID valuer.UUID) ([]*authtypes.UserRole, error) {
|
|
userRoles, err := module.userRoleStore.GetUserRolesByUserID(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, ur := range userRoles {
|
|
if ur.Role == nil {
|
|
return nil, errors.New(errors.TypeInternal, authtypes.ErrCodeRoleNotFound, "role not found for user role entry")
|
|
}
|
|
}
|
|
|
|
return userRoles, nil
|
|
}
|
|
|
|
func (module *getter) GetUserRoleByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*authtypes.UserRole, error) {
|
|
return module.userRoleStore.GetUserRoleByOrgIDAndID(ctx, orgID, id)
|
|
}
|
|
|
|
func (module *getter) GetResetPasswordTokenByOrgIDAndUserID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*types.ResetPasswordToken, error) {
|
|
return module.store.GetResetPasswordTokenByOrgIDAndUserID(ctx, orgID, userID)
|
|
}
|
|
|
|
func (module *getter) GetUsersByOrgIDAndRoleID(ctx context.Context, orgID valuer.UUID, roleID valuer.UUID) ([]*types.User, error) {
|
|
return module.store.GetUsersByOrgIDAndRoleID(ctx, orgID, roleID)
|
|
}
|
|
|
|
func (module *getter) VerifyResetPasswordToken(ctx context.Context, token string) error {
|
|
resetPasswordToken, err := module.store.GetResetPasswordToken(ctx, token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resetPasswordToken.IsExpired() {
|
|
return errors.New(errors.TypeUnauthenticated, types.ErrCodeResetPasswordTokenExpired, "reset password token has expired")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (module *getter) OnBeforeRoleDelete(ctx context.Context, orgID valuer.UUID, roleID valuer.UUID, _ string) error {
|
|
users, err := module.GetUsersByOrgIDAndRoleID(ctx, orgID, roleID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(users) > 0 {
|
|
return errors.New(errors.TypeInvalidInput, authtypes.ErrCodeRoleHasUserAssignees, "role has active user assignments, remove them before deleting")
|
|
}
|
|
return nil
|
|
}
|