mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-16 17:00:28 +01:00
Compare commits
8 Commits
chore/json
...
platform-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b00763a34 | ||
|
|
2bf8d5b758 | ||
|
|
3dd6db761c | ||
|
|
8b3eaf886f | ||
|
|
41a0058398 | ||
|
|
58c9b82e4f | ||
|
|
2a71a74523 | ||
|
|
71d2b84664 |
@@ -6068,9 +6068,9 @@ paths:
|
||||
- fields
|
||||
/api/v1/getResetPasswordToken/{id}:
|
||||
get:
|
||||
deprecated: false
|
||||
deprecated: true
|
||||
description: This endpoint returns the reset password token by id
|
||||
operationId: GetResetPasswordToken
|
||||
operationId: GetResetPasswordTokenDeprecated
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
@@ -10894,6 +10894,129 @@ paths:
|
||||
summary: Update user v2
|
||||
tags:
|
||||
- users
|
||||
/api/v2/users/{id}/reset_password_tokens:
|
||||
get:
|
||||
deprecated: false
|
||||
description: This endpoint returns the existing reset password token for a user.
|
||||
operationId: GetResetPasswordToken
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TypesResetPasswordToken'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Not Found
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Get reset password token for a user
|
||||
tags:
|
||||
- users
|
||||
post:
|
||||
deprecated: false
|
||||
description: This endpoint creates or regenerates a reset password token for
|
||||
a user. If a valid token exists, it is returned. If expired, a new one is
|
||||
created.
|
||||
operationId: CreateResetPasswordToken
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"201":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TypesResetPasswordToken'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: Created
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Not Found
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
summary: Create or regenerate reset password token for a user
|
||||
tags:
|
||||
- users
|
||||
/api/v2/users/{id}/roles:
|
||||
get:
|
||||
deprecated: false
|
||||
|
||||
@@ -5604,10 +5604,10 @@ export type GetFieldsValues200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetResetPasswordTokenPathParameters = {
|
||||
export type GetResetPasswordTokenDeprecatedPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetResetPasswordToken200 = {
|
||||
export type GetResetPasswordTokenDeprecated200 = {
|
||||
data: TypesResetPasswordTokenDTO;
|
||||
/**
|
||||
* @type string
|
||||
@@ -6579,6 +6579,28 @@ export type GetUser200 = {
|
||||
export type UpdateUserPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetResetPasswordTokenPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetResetPasswordToken200 = {
|
||||
data: TypesResetPasswordTokenDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type CreateResetPasswordTokenPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type CreateResetPasswordToken201 = {
|
||||
data: TypesResetPasswordTokenDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetRolesByUserIDPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
@@ -22,10 +22,14 @@ import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type {
|
||||
ChangePasswordPathParameters,
|
||||
CreateInvite201,
|
||||
CreateResetPasswordToken201,
|
||||
CreateResetPasswordTokenPathParameters,
|
||||
DeleteUserPathParameters,
|
||||
GetMyUser200,
|
||||
GetMyUserDeprecated200,
|
||||
GetResetPasswordToken200,
|
||||
GetResetPasswordTokenDeprecated200,
|
||||
GetResetPasswordTokenDeprecatedPathParameters,
|
||||
GetResetPasswordTokenPathParameters,
|
||||
GetRolesByUserID200,
|
||||
GetRolesByUserIDPathParameters,
|
||||
@@ -154,33 +158,34 @@ export const useChangePassword = <
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the reset password token by id
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
export const getResetPasswordToken = (
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
export const getResetPasswordTokenDeprecated = (
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetResetPasswordToken200>({
|
||||
return GeneratedAPIInstance<GetResetPasswordTokenDeprecated200>({
|
||||
url: `/api/v1/getResetPasswordToken/${id}`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenQueryKey = ({
|
||||
export const getGetResetPasswordTokenDeprecatedQueryKey = ({
|
||||
id,
|
||||
}: GetResetPasswordTokenPathParameters) => {
|
||||
}: GetResetPasswordTokenDeprecatedPathParameters) => {
|
||||
return [`/api/v1/getResetPasswordToken/${id}`] as const;
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
export const getGetResetPasswordTokenDeprecatedQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
@@ -189,11 +194,11 @@ export const getGetResetPasswordTokenQueryOptions = <
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetResetPasswordTokenQueryKey({ id });
|
||||
queryOptions?.queryKey ?? getGetResetPasswordTokenDeprecatedQueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>
|
||||
> = ({ signal }) => getResetPasswordToken({ id }, signal);
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>
|
||||
> = ({ signal }) => getResetPasswordTokenDeprecated({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
@@ -201,35 +206,39 @@ export const getGetResetPasswordTokenQueryOptions = <
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetResetPasswordTokenQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>
|
||||
export type GetResetPasswordTokenDeprecatedQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>
|
||||
>;
|
||||
export type GetResetPasswordTokenQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
export type GetResetPasswordTokenDeprecatedQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
|
||||
export function useGetResetPasswordToken<
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
export function useGetResetPasswordTokenDeprecated<
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
Awaited<ReturnType<typeof getResetPasswordTokenDeprecated>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetResetPasswordTokenQueryOptions({ id }, options);
|
||||
const queryOptions = getGetResetPasswordTokenDeprecatedQueryOptions(
|
||||
{ id },
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
@@ -241,15 +250,16 @@ export function useGetResetPasswordToken<
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Get reset password token
|
||||
*/
|
||||
export const invalidateGetResetPasswordToken = async (
|
||||
export const invalidateGetResetPasswordTokenDeprecated = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
{ id }: GetResetPasswordTokenDeprecatedPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetResetPasswordTokenQueryKey({ id }) },
|
||||
{ queryKey: getGetResetPasswordTokenDeprecatedQueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
@@ -1407,6 +1417,191 @@ export const useUpdateUser = <
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the existing reset password token for a user.
|
||||
* @summary Get reset password token for a user
|
||||
*/
|
||||
export const getResetPasswordToken = (
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetResetPasswordToken200>({
|
||||
url: `/api/v2/users/${id}/reset_password_tokens`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenQueryKey = ({
|
||||
id,
|
||||
}: GetResetPasswordTokenPathParameters) => {
|
||||
return [`/api/v2/users/${id}/reset_password_tokens`] as const;
|
||||
};
|
||||
|
||||
export const getGetResetPasswordTokenQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetResetPasswordTokenQueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>
|
||||
> = ({ signal }) => getResetPasswordToken({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetResetPasswordTokenQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>
|
||||
>;
|
||||
export type GetResetPasswordTokenQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get reset password token for a user
|
||||
*/
|
||||
|
||||
export function useGetResetPasswordToken<
|
||||
TData = Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getResetPasswordToken>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetResetPasswordTokenQueryOptions({ id }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get reset password token for a user
|
||||
*/
|
||||
export const invalidateGetResetPasswordToken = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetResetPasswordTokenPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetResetPasswordTokenQueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint creates or regenerates a reset password token for a user. If a valid token exists, it is returned. If expired, a new one is created.
|
||||
* @summary Create or regenerate reset password token for a user
|
||||
*/
|
||||
export const createResetPasswordToken = (
|
||||
{ id }: CreateResetPasswordTokenPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<CreateResetPasswordToken201>({
|
||||
url: `/api/v2/users/${id}/reset_password_tokens`,
|
||||
method: 'POST',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getCreateResetPasswordTokenMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createResetPasswordToken>>,
|
||||
TError,
|
||||
{ pathParams: CreateResetPasswordTokenPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createResetPasswordToken>>,
|
||||
TError,
|
||||
{ pathParams: CreateResetPasswordTokenPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['createResetPasswordToken'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof createResetPasswordToken>>,
|
||||
{ pathParams: CreateResetPasswordTokenPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return createResetPasswordToken(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type CreateResetPasswordTokenMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof createResetPasswordToken>>
|
||||
>;
|
||||
|
||||
export type CreateResetPasswordTokenMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Create or regenerate reset password token for a user
|
||||
*/
|
||||
export const useCreateResetPasswordToken = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof createResetPasswordToken>>,
|
||||
TError,
|
||||
{ pathParams: CreateResetPasswordTokenPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof createResetPasswordToken>>,
|
||||
TError,
|
||||
{ pathParams: CreateResetPasswordTokenPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getCreateResetPasswordTokenMutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the user roles by user id
|
||||
* @summary Get user roles
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import {
|
||||
GetResetPasswordToken,
|
||||
PayloadProps,
|
||||
Props,
|
||||
} from 'types/api/user/getResetPasswordToken';
|
||||
|
||||
const getResetPasswordToken = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponseV2<GetResetPasswordToken>> => {
|
||||
try {
|
||||
const response = await axios.get<PayloadProps>(
|
||||
`/getResetPasswordToken/${props.userId}`,
|
||||
);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getResetPasswordToken;
|
||||
@@ -10,8 +10,9 @@ import { Skeleton, Tooltip } from 'antd';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import type { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import {
|
||||
getResetPasswordToken,
|
||||
useCreateResetPasswordToken,
|
||||
useDeleteUser,
|
||||
useGetResetPasswordToken,
|
||||
useGetUser,
|
||||
useUpdateMyUserV2,
|
||||
useUpdateUser,
|
||||
@@ -55,6 +56,27 @@ function getDeleteTooltip(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getInviteButtonLabel(
|
||||
isLoading: boolean,
|
||||
existingToken: { expiresAt?: Date } | undefined,
|
||||
isExpired: boolean,
|
||||
notFound: boolean,
|
||||
): string {
|
||||
if (isLoading) {
|
||||
return 'Checking invite...';
|
||||
}
|
||||
if (existingToken && !isExpired) {
|
||||
return 'Copy Invite Link';
|
||||
}
|
||||
if (isExpired) {
|
||||
return 'Regenerate Invite Link';
|
||||
}
|
||||
if (notFound) {
|
||||
return 'Generate Invite Link';
|
||||
}
|
||||
return 'Copy Invite Link';
|
||||
}
|
||||
|
||||
function toSaveApiError(err: unknown): APIError {
|
||||
return (
|
||||
convertToApiError(err as AxiosError<RenderErrorResponseDTO>) ??
|
||||
@@ -83,9 +105,11 @@ function EditMemberDrawer({
|
||||
const [localRole, setLocalRole] = useState('');
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveErrors, setSaveErrors] = useState<SaveError[]>([]);
|
||||
const [isGeneratingLink, setIsGeneratingLink] = useState(false);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
const [resetLink, setResetLink] = useState<string | null>(null);
|
||||
const [resetLinkExpiresAt, setResetLinkExpiresAt] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [showResetLinkDialog, setShowResetLinkDialog] = useState(false);
|
||||
const [hasCopiedResetLink, setHasCopiedResetLink] = useState(false);
|
||||
const [linkType, setLinkType] = useState<'invite' | 'reset' | null>(null);
|
||||
@@ -121,6 +145,27 @@ function EditMemberDrawer({
|
||||
applyDiff,
|
||||
} = useMemberRoleManager(member?.id ?? '', open && !!member?.id);
|
||||
|
||||
// Token status query for invited users
|
||||
const {
|
||||
data: tokenQueryData,
|
||||
isLoading: isLoadingTokenStatus,
|
||||
isError: tokenNotFound,
|
||||
} = useGetResetPasswordToken(
|
||||
{ id: member?.id ?? '' },
|
||||
{ query: { enabled: open && !!member?.id && isInvited } },
|
||||
);
|
||||
|
||||
const existingToken = tokenQueryData?.data;
|
||||
const isTokenExpired =
|
||||
existingToken != null &&
|
||||
new Date(String(existingToken.expiresAt)) < new Date();
|
||||
|
||||
// Create/regenerate token mutation
|
||||
const {
|
||||
mutateAsync: createTokenMutation,
|
||||
isLoading: isGeneratingLink,
|
||||
} = useCreateResetPasswordToken();
|
||||
|
||||
const fetchedDisplayName =
|
||||
fetchedUser?.data?.displayName ?? member?.name ?? '';
|
||||
const fetchedUserId = fetchedUser?.data?.id;
|
||||
@@ -338,12 +383,21 @@ function EditMemberDrawer({
|
||||
if (!member) {
|
||||
return;
|
||||
}
|
||||
setIsGeneratingLink(true);
|
||||
try {
|
||||
const response = await getResetPasswordToken({ id: member.id });
|
||||
const response = await createTokenMutation({
|
||||
pathParams: { id: member.id },
|
||||
});
|
||||
if (response?.data?.token) {
|
||||
const link = `${window.location.origin}/password-reset?token=${response.data.token}`;
|
||||
setResetLink(link);
|
||||
setResetLinkExpiresAt(
|
||||
response.data.expiresAt
|
||||
? formatTimezoneAdjustedTimestamp(
|
||||
String(response.data.expiresAt),
|
||||
DATE_TIME_FORMATS.DASH_DATETIME,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
setHasCopiedResetLink(false);
|
||||
setLinkType(isInvited ? 'invite' : 'reset');
|
||||
setShowResetLinkDialog(true);
|
||||
@@ -359,10 +413,8 @@ function EditMemberDrawer({
|
||||
err as AxiosError<RenderErrorResponseDTO, unknown> | null,
|
||||
);
|
||||
showErrorModal(errMsg as APIError);
|
||||
} finally {
|
||||
setIsGeneratingLink(false);
|
||||
}
|
||||
}, [member, isInvited, onClose, showErrorModal]);
|
||||
}, [member, isInvited, onClose, showErrorModal, createTokenMutation]);
|
||||
|
||||
const [copyState, copyToClipboard] = useCopyToClipboard();
|
||||
const handleCopyResetLink = useCallback((): void => {
|
||||
@@ -568,12 +620,19 @@ function EditMemberDrawer({
|
||||
<Button
|
||||
className="edit-member-drawer__footer-btn edit-member-drawer__footer-btn--warning"
|
||||
onClick={handleGenerateResetLink}
|
||||
disabled={isGeneratingLink || isRootUser}
|
||||
disabled={isGeneratingLink || isRootUser || isLoadingTokenStatus}
|
||||
>
|
||||
<RefreshCw size={12} />
|
||||
{isGeneratingLink && 'Generating...'}
|
||||
{!isGeneratingLink && isInvited && 'Copy Invite Link'}
|
||||
{!isGeneratingLink && !isInvited && 'Generate Password Reset Link'}
|
||||
{isGeneratingLink
|
||||
? 'Generating...'
|
||||
: isInvited
|
||||
? getInviteButtonLabel(
|
||||
isLoadingTokenStatus,
|
||||
existingToken,
|
||||
isTokenExpired,
|
||||
tokenNotFound,
|
||||
)
|
||||
: 'Generate Password Reset Link'}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -623,6 +682,7 @@ function EditMemberDrawer({
|
||||
open={showResetLinkDialog}
|
||||
linkType={linkType}
|
||||
resetLink={resetLink}
|
||||
expiresAt={resetLinkExpiresAt}
|
||||
hasCopied={hasCopiedResetLink}
|
||||
onClose={(): void => {
|
||||
setShowResetLinkDialog(false);
|
||||
|
||||
@@ -6,6 +6,7 @@ interface ResetLinkDialogProps {
|
||||
open: boolean;
|
||||
linkType: 'invite' | 'reset' | null;
|
||||
resetLink: string | null;
|
||||
expiresAt: string | null;
|
||||
hasCopied: boolean;
|
||||
onClose: () => void;
|
||||
onCopy: () => void;
|
||||
@@ -15,6 +16,7 @@ function ResetLinkDialog({
|
||||
open,
|
||||
linkType,
|
||||
resetLink,
|
||||
expiresAt,
|
||||
hasCopied,
|
||||
onClose,
|
||||
onCopy,
|
||||
@@ -53,6 +55,11 @@ function ResetLinkDialog({
|
||||
{hasCopied ? 'Copied!' : 'Copy'}
|
||||
</Button>
|
||||
</div>
|
||||
{expiresAt && (
|
||||
<p className="reset-link-dialog__description">
|
||||
This link expires on {expiresAt}.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</DialogWrapper>
|
||||
);
|
||||
|
||||
@@ -2,8 +2,9 @@ import type { ReactNode } from 'react';
|
||||
import { toast } from '@signozhq/sonner';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
getResetPasswordToken,
|
||||
useCreateResetPasswordToken,
|
||||
useDeleteUser,
|
||||
useGetResetPasswordToken,
|
||||
useGetUser,
|
||||
useSetRoleByUserID,
|
||||
useUpdateMyUserV2,
|
||||
@@ -55,7 +56,8 @@ jest.mock('api/generated/services/users', () => ({
|
||||
useUpdateUser: jest.fn(),
|
||||
useUpdateMyUserV2: jest.fn(),
|
||||
useSetRoleByUserID: jest.fn(),
|
||||
getResetPasswordToken: jest.fn(),
|
||||
useGetResetPasswordToken: jest.fn(),
|
||||
useCreateResetPasswordToken: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('api/ErrorResponseHandlerForGeneratedAPIs', () => ({
|
||||
@@ -82,7 +84,7 @@ jest.mock('react-use', () => ({
|
||||
const ROLES_ENDPOINT = '*/api/v1/roles';
|
||||
|
||||
const mockDeleteMutate = jest.fn();
|
||||
const mockGetResetPasswordToken = jest.mocked(getResetPasswordToken);
|
||||
const mockCreateTokenMutateAsync = jest.fn();
|
||||
|
||||
const showErrorModal = jest.fn();
|
||||
jest.mock('providers/ErrorModalProvider', () => ({
|
||||
@@ -184,6 +186,31 @@ describe('EditMemberDrawer', () => {
|
||||
mutate: mockDeleteMutate,
|
||||
isLoading: false,
|
||||
});
|
||||
// Token query: valid token for invited members
|
||||
(useGetResetPasswordToken as jest.Mock).mockReturnValue({
|
||||
data: {
|
||||
data: {
|
||||
token: 'invite-tok-valid',
|
||||
id: 'token-1',
|
||||
expiresAt: new Date(Date.now() + 86400000).toISOString(),
|
||||
},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
});
|
||||
// Create token mutation
|
||||
mockCreateTokenMutateAsync.mockResolvedValue({
|
||||
status: 'success',
|
||||
data: {
|
||||
token: 'reset-tok-abc',
|
||||
id: 'user-1',
|
||||
expiresAt: new Date(Date.now() + 86400000).toISOString(),
|
||||
},
|
||||
});
|
||||
(useCreateResetPasswordToken as jest.Mock).mockReturnValue({
|
||||
mutateAsync: mockCreateTokenMutateAsync,
|
||||
isLoading: false,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -357,6 +384,40 @@ describe('EditMemberDrawer', () => {
|
||||
expect(screen.queryByText('Last Modified')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows "Regenerate Invite Link" when token is expired', () => {
|
||||
(useGetResetPasswordToken as jest.Mock).mockReturnValue({
|
||||
data: {
|
||||
data: {
|
||||
token: 'old-tok',
|
||||
id: 'token-1',
|
||||
expiresAt: new Date(Date.now() - 86400000).toISOString(), // expired yesterday
|
||||
},
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
});
|
||||
|
||||
renderDrawer({ member: invitedMember });
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /regenerate invite link/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows "Generate Invite Link" when no token exists', () => {
|
||||
(useGetResetPasswordToken as jest.Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
isError: true,
|
||||
});
|
||||
|
||||
renderDrawer({ member: invitedMember });
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: /generate invite link/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls deleteUser after confirming revoke invite for invited members', async () => {
|
||||
const onComplete = jest.fn();
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
@@ -609,7 +670,7 @@ describe('EditMemberDrawer', () => {
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not call getResetPasswordToken when Reset Link is clicked while disabled (root)', async () => {
|
||||
it('does not call createResetPasswordToken when Reset Link is clicked while disabled (root)', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
renderDrawer();
|
||||
|
||||
@@ -617,20 +678,16 @@ describe('EditMemberDrawer', () => {
|
||||
screen.getByRole('button', { name: /generate password reset link/i }),
|
||||
);
|
||||
|
||||
expect(mockGetResetPasswordToken).not.toHaveBeenCalled();
|
||||
expect(mockCreateTokenMutateAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Generate Password Reset Link', () => {
|
||||
beforeEach(() => {
|
||||
mockCopyToClipboard.mockClear();
|
||||
mockGetResetPasswordToken.mockResolvedValue({
|
||||
status: 'success',
|
||||
data: { token: 'reset-tok-abc', id: 'user-1' },
|
||||
});
|
||||
});
|
||||
|
||||
it('calls getResetPasswordToken and opens the reset link dialog with the generated link', async () => {
|
||||
it('calls POST and opens the reset link dialog with the generated link and expiry', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
|
||||
renderDrawer();
|
||||
@@ -642,11 +699,12 @@ describe('EditMemberDrawer', () => {
|
||||
const dialog = await screen.findByRole('dialog', {
|
||||
name: /password reset link/i,
|
||||
});
|
||||
expect(mockGetResetPasswordToken).toHaveBeenCalledWith({
|
||||
id: 'user-1',
|
||||
expect(mockCreateTokenMutateAsync).toHaveBeenCalledWith({
|
||||
pathParams: { id: 'user-1' },
|
||||
});
|
||||
expect(dialog).toBeInTheDocument();
|
||||
expect(dialog).toHaveTextContent('reset-tok-abc');
|
||||
expect(dialog).toHaveTextContent(/this link expires on/i);
|
||||
});
|
||||
|
||||
it('copies the link to clipboard and shows "Copied!" on the button', async () => {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { User } from 'types/reducer/app';
|
||||
|
||||
export interface Props {
|
||||
userId: User['userId'];
|
||||
}
|
||||
|
||||
export interface GetResetPasswordToken {
|
||||
token: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export interface PayloadProps {
|
||||
data: GetResetPasswordToken;
|
||||
status: string;
|
||||
}
|
||||
@@ -213,8 +213,8 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/getResetPasswordToken/{id}", handler.New(provider.authZ.AdminAccess(provider.userHandler.GetResetPasswordToken), handler.OpenAPIDef{
|
||||
ID: "GetResetPasswordToken",
|
||||
if err := router.Handle("/api/v1/getResetPasswordToken/{id}", handler.New(provider.authZ.AdminAccess(provider.userHandler.GetResetPasswordTokenDeprecated), handler.OpenAPIDef{
|
||||
ID: "GetResetPasswordTokenDeprecated",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get reset password token",
|
||||
Description: "This endpoint returns the reset password token by id",
|
||||
@@ -224,12 +224,46 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: true,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/{id}/reset_password_tokens", handler.New(provider.authZ.AdminAccess(provider.userHandler.GetResetPasswordToken), handler.OpenAPIDef{
|
||||
ID: "GetResetPasswordToken",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get reset password token for a user",
|
||||
Description: "This endpoint returns the existing reset password token for a user.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(types.ResetPasswordToken),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/{id}/reset_password_tokens", handler.New(provider.authZ.AdminAccess(provider.userHandler.CreateResetPasswordToken), handler.OpenAPIDef{
|
||||
ID: "CreateResetPasswordToken",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Create or regenerate reset password token for a user",
|
||||
Description: "This endpoint creates or regenerates a reset password token for a user. If a valid token exists, it is returned. If expired, a new one is created.",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(types.ResetPasswordToken),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/resetPassword", handler.New(provider.authZ.OpenAccess(provider.userHandler.ResetPassword), handler.OpenAPIDef{
|
||||
ID: "ResetPassword",
|
||||
Tags: []string{"users"},
|
||||
|
||||
@@ -218,6 +218,10 @@ func (module *getter) GetRolesByUserID(ctx context.Context, userID valuer.UUID)
|
||||
return userRoles, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func NewHandler(setter root.Setter, getter root.Getter) root.Handler {
|
||||
return &handler{setter: setter, getter: getter}
|
||||
}
|
||||
|
||||
func (h *handler) CreateInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) CreateInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -41,7 +41,7 @@ func (h *handler) CreateInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
invites, err := h.setter.CreateBulkInvite(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.IdentityID()), valuer.MustNewEmail(claims.Email), &types.PostableBulkInviteRequest{
|
||||
invites, err := handler.setter.CreateBulkInvite(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.IdentityID()), valuer.MustNewEmail(claims.Email), &types.PostableBulkInviteRequest{
|
||||
Invites: []types.PostableInvite{req},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -52,7 +52,7 @@ func (h *handler) CreateInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Success(rw, http.StatusCreated, invites[0])
|
||||
}
|
||||
|
||||
func (h *handler) CreateBulkInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) CreateBulkInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -74,7 +74,7 @@ func (h *handler) CreateBulkInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.setter.CreateBulkInvite(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.IdentityID()), valuer.MustNewEmail(claims.Email), &req)
|
||||
_, err = handler.setter.CreateBulkInvite(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.IdentityID()), valuer.MustNewEmail(claims.Email), &req)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
@@ -83,7 +83,7 @@ func (h *handler) CreateBulkInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Success(rw, http.StatusCreated, nil)
|
||||
}
|
||||
|
||||
func (h *handler) GetUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) GetUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -95,7 +95,7 @@ func (h *handler) GetUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetDeprecatedUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(id))
|
||||
user, err := handler.getter.GetDeprecatedUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(id))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -104,7 +104,7 @@ func (h *handler) GetUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (h *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -116,13 +116,13 @@ func (h *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID))
|
||||
user, err := handler.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
userRoles, err := h.getter.GetRolesByUserID(ctx, user.ID)
|
||||
userRoles, err := handler.getter.GetRolesByUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -136,7 +136,7 @@ func (h *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, userWithRoles)
|
||||
}
|
||||
|
||||
func (h *handler) GetMyUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) GetMyUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -146,7 +146,7 @@ func (h *handler) GetMyUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetDeprecatedUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID))
|
||||
user, err := handler.getter.GetDeprecatedUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -155,7 +155,7 @@ func (h *handler) GetMyUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (h *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -165,13 +165,13 @@ func (h *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID))
|
||||
user, err := handler.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
userRoles, err := h.getter.GetRolesByUserID(ctx, user.ID)
|
||||
userRoles, err := handler.getter.GetRolesByUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -185,7 +185,7 @@ func (h *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, userWithRoles)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) UpdateMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -201,7 +201,7 @@ func (h *handler) UpdateMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.setter.UpdateUser(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID), updatableUser)
|
||||
_, err = handler.setter.UpdateUser(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID), updatableUser)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -210,7 +210,7 @@ func (h *handler) UpdateMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) ListUsersDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) ListUsersDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -220,7 +220,7 @@ func (h *handler) ListUsersDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
users, err := h.getter.ListDeprecatedUsersByOrgID(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
users, err := handler.getter.ListDeprecatedUsersByOrgID(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -229,7 +229,7 @@ func (h *handler) ListUsersDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
func (h *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -239,7 +239,7 @@ func (h *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
users, err := h.getter.ListUsersByOrgID(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
users, err := handler.getter.ListUsersByOrgID(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -248,7 +248,7 @@ func (h *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) UpdateUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -266,7 +266,7 @@ func (h *handler) UpdateUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
updatedUser, err := h.setter.UpdateUserDeprecated(ctx, valuer.MustNewUUID(claims.OrgID), id, &user)
|
||||
updatedUser, err := handler.setter.UpdateUserDeprecated(ctx, valuer.MustNewUUID(claims.OrgID), id, &user)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -275,7 +275,7 @@ func (h *handler) UpdateUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, updatedUser)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -298,7 +298,7 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.setter.UpdateUser(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), updatableUser)
|
||||
_, err = handler.setter.UpdateUser(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), updatableUser)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -307,7 +307,7 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -319,7 +319,7 @@ func (h *handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.setter.DeleteUser(ctx, valuer.MustNewUUID(claims.OrgID), id, claims.IdentityID()); err != nil {
|
||||
if err := handler.setter.DeleteUser(ctx, valuer.MustNewUUID(claims.OrgID), id, claims.IdentityID()); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func (h *handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (handler *handler) GetResetPasswordToken(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) GetResetPasswordTokenDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -354,6 +354,62 @@ func (handler *handler) GetResetPasswordToken(w http.ResponseWriter, r *http.Req
|
||||
render.Success(w, http.StatusOK, token)
|
||||
}
|
||||
|
||||
func (handler *handler) GetResetPasswordToken(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
userID, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := handler.getter.GetResetPasswordTokenByOrgIDAndUserID(ctx, valuer.MustNewUUID(claims.OrgID), userID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, token)
|
||||
}
|
||||
|
||||
func (handler *handler) CreateResetPasswordToken(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
userID, err := valuer.NewUUID(mux.Vars(r)["id"])
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := handler.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), userID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := handler.setter.GetOrCreateResetPasswordToken(ctx, user.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusCreated, token)
|
||||
}
|
||||
|
||||
func (handler *handler) ResetPassword(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -392,7 +448,7 @@ func (handler *handler) ChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -402,7 +458,7 @@ func (h *handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err := h.setter.ForgotPassword(ctx, req.OrgID, req.Email, req.FrontendBaseURL)
|
||||
err := handler.setter.ForgotPassword(ctx, req.OrgID, req.Email, req.FrontendBaseURL)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -411,7 +467,7 @@ func (h *handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) GetRolesByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) GetRolesByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -423,13 +479,13 @@ func (h *handler) GetRolesByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID))
|
||||
user, err := handler.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
userRoles, err := h.getter.GetRolesByUserID(ctx, user.ID)
|
||||
userRoles, err := handler.getter.GetRolesByUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -443,7 +499,7 @@ func (h *handler) GetRolesByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, roles)
|
||||
}
|
||||
|
||||
func (h *handler) SetRoleByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) SetRoleByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -471,7 +527,7 @@ func (h *handler) SetRoleByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.setter.AddUserRole(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), postableRole.Name); err != nil {
|
||||
if err := handler.setter.AddUserRole(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), postableRole.Name); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
@@ -479,7 +535,7 @@ func (h *handler) SetRoleByUserID(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
func (h *handler) RemoveUserRoleByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) RemoveUserRoleByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -497,7 +553,7 @@ func (h *handler) RemoveUserRoleByRoleID(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.setter.RemoveUserRole(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), valuer.MustNewUUID(roleID)); err != nil {
|
||||
if err := handler.setter.RemoveUserRole(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), valuer.MustNewUUID(roleID)); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
@@ -505,7 +561,7 @@ func (h *handler) RemoveUserRoleByRoleID(w http.ResponseWriter, r *http.Request)
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) GetUsersByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *handler) GetUsersByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -517,7 +573,7 @@ func (h *handler) GetUsersByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
users, err := h.getter.GetUsersByOrgIDAndRoleID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(roleID))
|
||||
users, err := handler.getter.GetUsersByOrgIDAndRoleID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(roleID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
|
||||
@@ -359,6 +359,26 @@ func (store *store) GetResetPasswordTokenByPasswordID(ctx context.Context, passw
|
||||
return resetPasswordToken, nil
|
||||
}
|
||||
|
||||
func (store *store) GetResetPasswordTokenByOrgIDAndUserID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*types.ResetPasswordToken, error) {
|
||||
resetPasswordToken := new(types.ResetPasswordToken)
|
||||
|
||||
err := store.
|
||||
sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(resetPasswordToken).
|
||||
Join("JOIN factor_password ON factor_password.id = reset_password_token.password_id").
|
||||
Join("JOIN users ON users.id = factor_password.user_id").
|
||||
Where("factor_password.user_id = ?", userID).
|
||||
Where("users.org_id = ?", orgID).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, store.sqlstore.WrapNotFoundErrf(err, types.ErrResetPasswordTokenNotFound, "reset password token for user %s does not exist", userID)
|
||||
}
|
||||
|
||||
return resetPasswordToken, nil
|
||||
}
|
||||
|
||||
func (store *store) DeleteResetPasswordTokenByPasswordID(ctx context.Context, passwordID valuer.UUID) error {
|
||||
_, err := store.sqlstore.BunDBCtx(ctx).NewDelete().
|
||||
Model(&types.ResetPasswordToken{}).
|
||||
|
||||
@@ -80,6 +80,9 @@ type Getter interface {
|
||||
// Get factor password by user id.
|
||||
GetFactorPasswordByUserID(context.Context, valuer.UUID) (*types.FactorPassword, error)
|
||||
|
||||
// Get reset password token by org id and user id.
|
||||
GetResetPasswordTokenByOrgIDAndUserID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*types.ResetPasswordToken, error)
|
||||
|
||||
// Gets single Non-Deleted user by email and org id
|
||||
GetNonDeletedUserByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) (*types.User, error)
|
||||
|
||||
@@ -112,7 +115,9 @@ type Handler interface {
|
||||
GetUsersByRoleID(http.ResponseWriter, *http.Request)
|
||||
|
||||
// Reset Password
|
||||
GetResetPasswordTokenDeprecated(http.ResponseWriter, *http.Request)
|
||||
GetResetPasswordToken(http.ResponseWriter, *http.Request)
|
||||
CreateResetPasswordToken(http.ResponseWriter, *http.Request)
|
||||
ResetPassword(http.ResponseWriter, *http.Request)
|
||||
ChangePassword(http.ResponseWriter, *http.Request)
|
||||
ForgotPassword(http.ResponseWriter, *http.Request)
|
||||
|
||||
@@ -284,6 +284,7 @@ type UserStore interface {
|
||||
GetPasswordByUserID(ctx context.Context, userID valuer.UUID) (*FactorPassword, error)
|
||||
GetResetPasswordToken(ctx context.Context, token string) (*ResetPasswordToken, error)
|
||||
GetResetPasswordTokenByPasswordID(ctx context.Context, passwordID valuer.UUID) (*ResetPasswordToken, error)
|
||||
GetResetPasswordTokenByOrgIDAndUserID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*ResetPasswordToken, error)
|
||||
DeleteResetPasswordTokenByPasswordID(ctx context.Context, passwordID valuer.UUID) error
|
||||
UpdatePassword(ctx context.Context, password *FactorPassword) error
|
||||
|
||||
|
||||
@@ -91,17 +91,42 @@ def test_reset_password(
|
||||
# Get the user id via v2
|
||||
found_user = find_user_by_email(signoz, admin_token, PASSWORD_USER_EMAIL)
|
||||
|
||||
response = requests.get(
|
||||
# Create a reset password token via v2 POST
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(
|
||||
f"/api/v1/getResetPasswordToken/{found_user['id']}"
|
||||
f"/api/v2/users/{found_user['id']}/reset_password_tokens"
|
||||
),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.status_code == HTTPStatus.CREATED, response.text
|
||||
token_data = response.json()["data"]
|
||||
assert "token" in token_data
|
||||
assert "expiresAt" in token_data
|
||||
token = token_data["token"]
|
||||
|
||||
token = response.json()["data"]["token"]
|
||||
# Calling POST again should return the same token (still valid)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(
|
||||
f"/api/v2/users/{found_user['id']}/reset_password_tokens"
|
||||
),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.CREATED, response.text
|
||||
assert response.json()["data"]["token"] == token
|
||||
|
||||
# GET should also return the same token
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get(
|
||||
f"/api/v2/users/{found_user['id']}/reset_password_tokens"
|
||||
),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
assert response.json()["data"]["token"] == token
|
||||
|
||||
# Reset the password with a bad password which should fail
|
||||
response = requests.post(
|
||||
@@ -140,18 +165,29 @@ def test_reset_password_with_no_password(
|
||||
)
|
||||
assert result.rowcount == 1
|
||||
|
||||
# Generate a new reset password token
|
||||
# GET should return 404 since there's no password (and thus no token)
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get(
|
||||
f"/api/v1/getResetPasswordToken/{found_user['id']}"
|
||||
f"/api/v2/users/{found_user['id']}/reset_password_tokens"
|
||||
),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NOT_FOUND, response.text
|
||||
|
||||
# Generate a new reset password token via v2 POST
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(
|
||||
f"/api/v2/users/{found_user['id']}/reset_password_tokens"
|
||||
),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
token = response.json()["data"]["token"]
|
||||
assert response.status_code == HTTPStatus.CREATED, response.text
|
||||
token_data = response.json()["data"]
|
||||
assert "expiresAt" in token_data
|
||||
token = token_data["token"]
|
||||
|
||||
# Reset the password with a good password
|
||||
response = requests.post(
|
||||
@@ -262,32 +298,22 @@ def test_forgot_password_creates_reset_token(
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Verify reset password token was created by querying the database
|
||||
# Verify reset password token was created via the v2 GET endpoint
|
||||
found_user = find_user_by_email(signoz, admin_token, forgot_email)
|
||||
|
||||
reset_token = None
|
||||
# Query the database directly to get the reset password token
|
||||
# First get the password_id from factor_password, then get the token
|
||||
with signoz.sqlstore.conn.connect() as conn:
|
||||
result = conn.execute(
|
||||
sql.text(
|
||||
"""
|
||||
SELECT rpt.token
|
||||
FROM reset_password_token rpt
|
||||
JOIN factor_password fp ON rpt.password_id = fp.id
|
||||
WHERE fp.user_id = :user_id
|
||||
"""
|
||||
),
|
||||
{"user_id": found_user["id"]},
|
||||
)
|
||||
row = result.fetchone()
|
||||
assert (
|
||||
row is not None
|
||||
), "Reset password token should exist after calling forgotPassword"
|
||||
reset_token = row[0]
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get(
|
||||
f"/api/v2/users/{found_user['id']}/reset_password_tokens"
|
||||
),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
token_data = response.json()["data"]
|
||||
reset_token = token_data["token"]
|
||||
assert reset_token is not None
|
||||
assert reset_token != ""
|
||||
assert "expiresAt" in token_data
|
||||
|
||||
# Reset password with a valid strong password
|
||||
response = requests.post(
|
||||
|
||||
Reference in New Issue
Block a user