mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-24 13:20:27 +00:00
Compare commits
21 Commits
refactor/c
...
feat/v2-us
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
259a53ac70 | ||
|
|
d263e3d00d | ||
|
|
bebde04363 | ||
|
|
546edf12fa | ||
|
|
3266e0a293 | ||
|
|
a1c650374b | ||
|
|
843b4e2886 | ||
|
|
fad87b6d3c | ||
|
|
e45b296236 | ||
|
|
71d25adecd | ||
|
|
6889d7837c | ||
|
|
6df2b50e29 | ||
|
|
6a2bd68795 | ||
|
|
28654dae64 | ||
|
|
bddc878143 | ||
|
|
c84d521a10 | ||
|
|
1b570bffb5 | ||
|
|
560fe9a072 | ||
|
|
95c0c77d7c | ||
|
|
0e0a87505e | ||
|
|
216d01f2c2 |
1206
docs/api/openapi.yml
1206
docs/api/openapi.yml
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -437,152 +437,41 @@ export interface AuthtypesUpdateableAuthDomainDTO {
|
||||
config?: AuthtypesAuthDomainConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSAccountConfigDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
regions: string[];
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAWSCollectionStrategyDTOS3Buckets = {
|
||||
[key: string]: string[];
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSCollectionStrategyDTO {
|
||||
aws_logs?: CloudintegrationtypesAWSLogsStrategyDTO;
|
||||
aws_metrics?: CloudintegrationtypesAWSMetricsStrategyDTO;
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
s3_buckets?: CloudintegrationtypesAWSCollectionStrategyDTOS3Buckets;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSConnectionArtifactDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
connectionURL: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSConnectionArtifactRequestDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
deploymentRegion: string;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
regions: string[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSIntegrationConfigDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
enabledRegions: string[];
|
||||
telemetry: CloudintegrationtypesAWSCollectionStrategyDTO;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem = {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
filter_pattern?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
log_group_name_prefix?: string;
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSLogsStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
cloudwatch_logs_subscriptions?:
|
||||
| CloudintegrationtypesAWSLogsStrategyDTOCloudwatchLogsSubscriptionsItem[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
MetricNames?: string[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
Namespace?: string;
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSMetricsStrategyDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
cloudwatch_metric_stream_filters?:
|
||||
| CloudintegrationtypesAWSMetricsStrategyDTOCloudwatchMetricStreamFiltersItem[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSServiceConfigDTO {
|
||||
logs?: CloudintegrationtypesAWSServiceLogsConfigDTO;
|
||||
metrics?: CloudintegrationtypesAWSServiceMetricsConfigDTO;
|
||||
}
|
||||
|
||||
export type CloudintegrationtypesAWSServiceLogsConfigDTOS3Buckets = {
|
||||
[key: string]: string[];
|
||||
};
|
||||
|
||||
export interface CloudintegrationtypesAWSServiceLogsConfigDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
s3_buckets?: CloudintegrationtypesAWSServiceLogsConfigDTOS3Buckets;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAWSServiceMetricsConfigDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAccountDTO {
|
||||
agentReport: CloudintegrationtypesAgentReportDTO;
|
||||
config: CloudintegrationtypesAccountConfigDTO;
|
||||
export interface AuthtypesUserWithRolesDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
createdAt?: Date;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type string
|
||||
* @type boolean
|
||||
*/
|
||||
orgId: string;
|
||||
isRoot?: boolean;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
provider: string;
|
||||
orgId?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
providerAccountId: string | null;
|
||||
roles?: AuthtypesRoleDTO[] | null;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
* @nullable true
|
||||
*/
|
||||
removedAt: Date | null;
|
||||
status?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
@@ -590,283 +479,6 @@ export interface CloudintegrationtypesAccountDTO {
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesAccountConfigDTO {
|
||||
aws: CloudintegrationtypesAWSAccountConfigDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesAgentReportDTOData = {
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesAgentReportDTO = {
|
||||
/**
|
||||
* @type object
|
||||
* @nullable true
|
||||
*/
|
||||
data: CloudintegrationtypesAgentReportDTOData;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
timestampMillis: number;
|
||||
} | null;
|
||||
|
||||
export interface CloudintegrationtypesAssetsDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
dashboards?: CloudintegrationtypesDashboardDTO[] | null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesCollectedLogAttributeDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesCollectedMetricDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
unit?: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesCollectionStrategyDTO {
|
||||
aws: CloudintegrationtypesAWSCollectionStrategyDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesConnectionArtifactDTO {
|
||||
aws: CloudintegrationtypesAWSConnectionArtifactDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesConnectionArtifactRequestDTO {
|
||||
aws: CloudintegrationtypesAWSConnectionArtifactRequestDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesDashboardDTO {
|
||||
definition?: DashboardtypesStorableDashboardDataDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesDataCollectedDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
logs?: CloudintegrationtypesCollectedLogAttributeDTO[] | null;
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
metrics?: CloudintegrationtypesCollectedMetricDTO[] | null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesGettableAccountWithArtifactDTO {
|
||||
connectionArtifact: CloudintegrationtypesConnectionArtifactDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesGettableAccountsDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
accounts: CloudintegrationtypesAccountDTO[];
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesGettableAgentCheckInResponseDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
account_id: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
cloud_account_id: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
cloudIntegrationId: string;
|
||||
integration_config: CloudintegrationtypesIntegrationConfigDTO;
|
||||
integrationConfig: CloudintegrationtypesProviderIntegrationConfigDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
providerAccountId: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
* @nullable true
|
||||
*/
|
||||
removed_at: Date | null;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
* @nullable true
|
||||
*/
|
||||
removedAt: Date | null;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesGettableServicesMetadataDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
services: CloudintegrationtypesServiceMetadataDTO[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesIntegrationConfigDTO = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
enabled_regions: string[];
|
||||
telemetry: CloudintegrationtypesAWSCollectionStrategyDTO;
|
||||
} | null;
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type CloudintegrationtypesPostableAgentCheckInRequestDTOData = {
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
|
||||
export interface CloudintegrationtypesPostableAgentCheckInRequestDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
account_id?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
cloud_account_id?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
cloudIntegrationId?: string;
|
||||
/**
|
||||
* @type object
|
||||
* @nullable true
|
||||
*/
|
||||
data: CloudintegrationtypesPostableAgentCheckInRequestDTOData;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
providerAccountId?: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesProviderIntegrationConfigDTO {
|
||||
aws: CloudintegrationtypesAWSIntegrationConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesServiceDTO {
|
||||
assets: CloudintegrationtypesAssetsDTO;
|
||||
dataCollected: CloudintegrationtypesDataCollectedDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
icon: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
overview: string;
|
||||
serviceConfig?: CloudintegrationtypesServiceConfigDTO;
|
||||
supported_signals: CloudintegrationtypesSupportedSignalsDTO;
|
||||
telemetryCollectionStrategy: CloudintegrationtypesCollectionStrategyDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesServiceConfigDTO {
|
||||
aws: CloudintegrationtypesAWSServiceConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesServiceMetadataDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
icon: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesSupportedSignalsDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
logs?: boolean;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
metrics?: boolean;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesUpdatableAccountDTO {
|
||||
config: CloudintegrationtypesAccountConfigDTO;
|
||||
}
|
||||
|
||||
export interface CloudintegrationtypesUpdatableServiceDTO {
|
||||
config: CloudintegrationtypesServiceConfigDTO;
|
||||
}
|
||||
|
||||
export interface DashboardtypesDashboardDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -3144,6 +2756,24 @@ export interface TypesStorableAPIKeyDTO {
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export interface TypesUpdatableSelfUserDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
export interface TypesUpdatableUserDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
displayName: string;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
roleNames: string[];
|
||||
}
|
||||
|
||||
export interface TypesUserDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -3288,97 +2918,6 @@ export type AuthzResources200 = {
|
||||
export type ChangePasswordPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type AgentCheckInDeprecatedPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type AgentCheckInDeprecated200 = {
|
||||
data: CloudintegrationtypesGettableAgentCheckInResponseDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListAccountsPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type ListAccounts200 = {
|
||||
data: CloudintegrationtypesGettableAccountsDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type CreateAccountPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type CreateAccount200 = {
|
||||
data: CloudintegrationtypesGettableAccountWithArtifactDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type DisconnectAccountPathParameters = {
|
||||
cloudProvider: string;
|
||||
id: string;
|
||||
};
|
||||
export type GetAccountPathParameters = {
|
||||
cloudProvider: string;
|
||||
id: string;
|
||||
};
|
||||
export type GetAccount200 = {
|
||||
data: CloudintegrationtypesAccountDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type UpdateAccountPathParameters = {
|
||||
cloudProvider: string;
|
||||
id: string;
|
||||
};
|
||||
export type AgentCheckInPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type AgentCheckIn200 = {
|
||||
data: CloudintegrationtypesGettableAgentCheckInResponseDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListServicesMetadataPathParameters = {
|
||||
cloudProvider: string;
|
||||
};
|
||||
export type ListServicesMetadata200 = {
|
||||
data: CloudintegrationtypesGettableServicesMetadataDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetServicePathParameters = {
|
||||
cloudProvider: string;
|
||||
serviceId: string;
|
||||
};
|
||||
export type GetService200 = {
|
||||
data: CloudintegrationtypesServiceDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type UpdateServicePathParameters = {
|
||||
cloudProvider: string;
|
||||
serviceId: string;
|
||||
};
|
||||
export type CreateSessionByGoogleCallback303 = {
|
||||
data: AuthtypesGettableTokenDTO;
|
||||
/**
|
||||
@@ -4207,6 +3746,42 @@ export type RotateSession200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListUsersV2200 = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
data: TypesUserDTO[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type UpdateUserV2PathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetUserRolesPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetUserRoles200 = {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
data: AuthtypesRoleDTO[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMyUserV2200 = {
|
||||
data: AuthtypesUserWithRolesDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetHosts200 = {
|
||||
data: ZeustypesGettableHostDTO;
|
||||
/**
|
||||
|
||||
@@ -25,12 +25,16 @@ import type {
|
||||
CreateInvite201,
|
||||
DeleteUserPathParameters,
|
||||
GetMyUser200,
|
||||
GetMyUserV2200,
|
||||
GetResetPasswordToken200,
|
||||
GetResetPasswordTokenPathParameters,
|
||||
GetUser200,
|
||||
GetUserPathParameters,
|
||||
GetUserRoles200,
|
||||
GetUserRolesPathParameters,
|
||||
ListAPIKeys200,
|
||||
ListUsers200,
|
||||
ListUsersV2200,
|
||||
RenderErrorResponseDTO,
|
||||
RevokeAPIKeyPathParameters,
|
||||
TypesChangePasswordRequestDTO,
|
||||
@@ -41,9 +45,12 @@ import type {
|
||||
TypesPostableInviteDTO,
|
||||
TypesPostableResetPasswordDTO,
|
||||
TypesStorableAPIKeyDTO,
|
||||
TypesUpdatableSelfUserDTO,
|
||||
TypesUpdatableUserDTO,
|
||||
UpdateAPIKeyPathParameters,
|
||||
UpdateUser200,
|
||||
UpdateUserPathParameters,
|
||||
UpdateUserV2PathParameters,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
/**
|
||||
@@ -1345,3 +1352,454 @@ export const useForgotPassword = <
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* This endpoint lists all users for the organization
|
||||
* @summary List users v2
|
||||
*/
|
||||
export const listUsersV2 = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<ListUsersV2200>({
|
||||
url: `/api/v2/users`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListUsersV2QueryKey = () => {
|
||||
return [`/api/v2/users`] as const;
|
||||
};
|
||||
|
||||
export const getListUsersV2QueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof listUsersV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getListUsersV2QueryKey();
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof listUsersV2>>> = ({
|
||||
signal,
|
||||
}) => listUsersV2(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersV2>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type ListUsersV2QueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listUsersV2>>
|
||||
>;
|
||||
export type ListUsersV2QueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary List users v2
|
||||
*/
|
||||
|
||||
export function useListUsersV2<
|
||||
TData = Awaited<ReturnType<typeof listUsersV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof listUsersV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getListUsersV2QueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary List users v2
|
||||
*/
|
||||
export const invalidateListUsersV2 = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getListUsersV2QueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint updates the user by id
|
||||
* @summary Update user v2
|
||||
*/
|
||||
export const updateUserV2 = (
|
||||
{ id }: UpdateUserV2PathParameters,
|
||||
typesUpdatableUserDTO: BodyType<TypesUpdatableUserDTO>,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/users/${id}`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesUpdatableUserDTO,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateUserV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateUserV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateUserV2PathParameters;
|
||||
data: BodyType<TypesUpdatableUserDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateUserV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateUserV2PathParameters;
|
||||
data: BodyType<TypesUpdatableUserDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateUserV2'];
|
||||
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 updateUserV2>>,
|
||||
{
|
||||
pathParams: UpdateUserV2PathParameters;
|
||||
data: BodyType<TypesUpdatableUserDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return updateUserV2(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateUserV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateUserV2>>
|
||||
>;
|
||||
export type UpdateUserV2MutationBody = BodyType<TypesUpdatableUserDTO>;
|
||||
export type UpdateUserV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update user v2
|
||||
*/
|
||||
export const useUpdateUserV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateUserV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateUserV2PathParameters;
|
||||
data: BodyType<TypesUpdatableUserDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateUserV2>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: UpdateUserV2PathParameters;
|
||||
data: BodyType<TypesUpdatableUserDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getUpdateUserV2MutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the user roles by user id
|
||||
* @summary Get user roles
|
||||
*/
|
||||
export const getUserRoles = (
|
||||
{ id }: GetUserRolesPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetUserRoles200>({
|
||||
url: `/api/v2/users/${id}/roles`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetUserRolesQueryKey = ({ id }: GetUserRolesPathParameters) => {
|
||||
return [`/api/v2/users/${id}/roles`] as const;
|
||||
};
|
||||
|
||||
export const getGetUserRolesQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getUserRoles>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetUserRolesPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getUserRoles>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetUserRolesQueryKey({ id });
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getUserRoles>>> = ({
|
||||
signal,
|
||||
}) => getUserRoles({ id }, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getUserRoles>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetUserRolesQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getUserRoles>>
|
||||
>;
|
||||
export type GetUserRolesQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get user roles
|
||||
*/
|
||||
|
||||
export function useGetUserRoles<
|
||||
TData = Awaited<ReturnType<typeof getUserRoles>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetUserRolesPathParameters,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getUserRoles>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetUserRolesQueryOptions({ id }, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get user roles
|
||||
*/
|
||||
export const invalidateGetUserRoles = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetUserRolesPathParameters,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetUserRolesQueryKey({ id }) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint returns the user I belong to
|
||||
* @summary Get my user v2
|
||||
*/
|
||||
export const getMyUserV2 = (signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<GetMyUserV2200>({
|
||||
url: `/api/v2/users/me`,
|
||||
method: 'GET',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetMyUserV2QueryKey = () => {
|
||||
return [`/api/v2/users/me`] as const;
|
||||
};
|
||||
|
||||
export const getGetMyUserV2QueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getMyUserV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getMyUserV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetMyUserV2QueryKey();
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getMyUserV2>>> = ({
|
||||
signal,
|
||||
}) => getMyUserV2(signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getMyUserV2>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetMyUserV2QueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getMyUserV2>>
|
||||
>;
|
||||
export type GetMyUserV2QueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get my user v2
|
||||
*/
|
||||
|
||||
export function useGetMyUserV2<
|
||||
TData = Awaited<ReturnType<typeof getMyUserV2>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getMyUserV2>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetMyUserV2QueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get my user v2
|
||||
*/
|
||||
export const invalidateGetMyUserV2 = async (
|
||||
queryClient: QueryClient,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetMyUserV2QueryKey() },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* This endpoint updates the user I belong to
|
||||
* @summary Update my user v2
|
||||
*/
|
||||
export const updateMyUserV2 = (
|
||||
typesUpdatableSelfUserDTO: BodyType<TypesUpdatableSelfUserDTO>,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/users/me`,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: typesUpdatableSelfUserDTO,
|
||||
});
|
||||
};
|
||||
|
||||
export const getUpdateMyUserV2MutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateMyUserV2>>,
|
||||
TError,
|
||||
{ data: BodyType<TypesUpdatableSelfUserDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateMyUserV2>>,
|
||||
TError,
|
||||
{ data: BodyType<TypesUpdatableSelfUserDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['updateMyUserV2'];
|
||||
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 updateMyUserV2>>,
|
||||
{ data: BodyType<TypesUpdatableSelfUserDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return updateMyUserV2(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type UpdateMyUserV2MutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof updateMyUserV2>>
|
||||
>;
|
||||
export type UpdateMyUserV2MutationBody = BodyType<TypesUpdatableSelfUserDTO>;
|
||||
export type UpdateMyUserV2MutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Update my user v2
|
||||
*/
|
||||
export const useUpdateMyUserV2 = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof updateMyUserV2>>,
|
||||
TError,
|
||||
{ data: BodyType<TypesUpdatableSelfUserDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof updateMyUserV2>>,
|
||||
TError,
|
||||
{ data: BodyType<TypesUpdatableSelfUserDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getUpdateMyUserV2MutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
|
||||
@@ -62,6 +62,9 @@ export const getVolumeQueryPayload = (
|
||||
const k8sPVCNameKey = dotMetricsEnabled
|
||||
? 'k8s.persistentvolumeclaim.name'
|
||||
: 'k8s_persistentvolumeclaim_name';
|
||||
const legendTemplate = dotMetricsEnabled
|
||||
? '{{k8s.namespace.name}}-{{k8s.pod.name}}'
|
||||
: '{{k8s_namespace_name}}-{{k8s_pod_name}}';
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -133,7 +136,7 @@ export const getVolumeQueryPayload = (
|
||||
functions: [],
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: 'Available',
|
||||
legend: legendTemplate,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
@@ -225,7 +228,7 @@ export const getVolumeQueryPayload = (
|
||||
functions: [],
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: 'Capacity',
|
||||
legend: legendTemplate,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
@@ -316,7 +319,7 @@ export const getVolumeQueryPayload = (
|
||||
},
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: 'Inodes Used',
|
||||
legend: legendTemplate,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
@@ -408,7 +411,7 @@ export const getVolumeQueryPayload = (
|
||||
},
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: 'Total Inodes',
|
||||
legend: legendTemplate,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
@@ -500,7 +503,7 @@ export const getVolumeQueryPayload = (
|
||||
},
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend: 'Inodes Free',
|
||||
legend: legendTemplate,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
package signozapiserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
citypes "github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (provider *provider) addCloudIntegrationRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.CreateAccount),
|
||||
handler.OpenAPIDef{
|
||||
ID: "CreateAccount",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Create account",
|
||||
Description: "This endpoint creates a new cloud integration account for the specified cloud provider",
|
||||
Request: new(citypes.PostableConnectionArtifact),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.GettableAccountWithArtifact),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.ListAccounts),
|
||||
handler.OpenAPIDef{
|
||||
ID: "ListAccounts",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "List accounts",
|
||||
Description: "This endpoint lists the accounts for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.GettableAccounts),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts/{id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.GetAccount),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetAccount",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Get account",
|
||||
Description: "This endpoint gets an account for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.GettableAccount),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts/{id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.UpdateAccount),
|
||||
handler.OpenAPIDef{
|
||||
ID: "UpdateAccount",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Update account",
|
||||
Description: "This endpoint updates an account for the specified cloud provider",
|
||||
Request: new(citypes.UpdatableAccount),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts/{id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.DisconnectAccount),
|
||||
handler.OpenAPIDef{
|
||||
ID: "DisconnectAccount",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Disconnect account",
|
||||
Description: "This endpoint disconnects an account for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/services", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.ListServicesMetadata),
|
||||
handler.OpenAPIDef{
|
||||
ID: "ListServicesMetadata",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "List services metadata",
|
||||
Description: "This endpoint lists the services metadata for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.GettableServicesMetadata),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/services/{service_id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.GetService),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetService",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Get service",
|
||||
Description: "This endpoint gets a service for the specified cloud provider",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(citypes.GettableService),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/services/{service_id}", handler.New(
|
||||
provider.authZ.AdminAccess(provider.cloudIntegrationHandler.UpdateService),
|
||||
handler.OpenAPIDef{
|
||||
ID: "UpdateService",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Update service",
|
||||
Description: "This endpoint updates a service for the specified cloud provider",
|
||||
Request: new(citypes.UpdatableService),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
},
|
||||
)).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Agent check-in endpoint is kept same as older one to maintain backward compatibility with already deployed agents.
|
||||
// In the future, this endpoint will be deprecated and a new endpoint will be introduced for consistency with above endpoints.
|
||||
if err := router.Handle("/api/v1/cloud-integrations/{cloud_provider}/agent-check-in", handler.New(
|
||||
provider.authZ.ViewAccess(provider.cloudIntegrationHandler.AgentCheckIn),
|
||||
handler.OpenAPIDef{
|
||||
ID: "AgentCheckInDeprecated",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Agent check-in",
|
||||
Description: "[Deprecated] This endpoint is called by the deployed agent to check in",
|
||||
Request: new(citypes.PostableAgentCheckInRequest),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.GettableAgentCheckInResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: true, // this endpoint will be deprecated in future
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer), // agent role is viewer
|
||||
},
|
||||
)).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/cloud_integrations/{cloud_provider}/accounts/check_in", handler.New(
|
||||
provider.authZ.ViewAccess(provider.cloudIntegrationHandler.AgentCheckIn),
|
||||
handler.OpenAPIDef{
|
||||
ID: "AgentCheckIn",
|
||||
Tags: []string{"cloudintegration"},
|
||||
Summary: "Agent check-in",
|
||||
Description: "This endpoint is called by the deployed agent to check in",
|
||||
Request: new(citypes.PostableAgentCheckInRequest),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(citypes.GettableAgentCheckInResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer), // agent role is viewer
|
||||
},
|
||||
)).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||
"github.com/SigNoz/signoz/pkg/modules/authdomain"
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/fields"
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
|
||||
@@ -31,30 +30,29 @@ import (
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
config apiserver.Config
|
||||
settings factory.ScopedProviderSettings
|
||||
router *mux.Router
|
||||
authZ *middleware.AuthZ
|
||||
orgHandler organization.Handler
|
||||
userHandler user.Handler
|
||||
sessionHandler session.Handler
|
||||
authDomainHandler authdomain.Handler
|
||||
preferenceHandler preference.Handler
|
||||
globalHandler global.Handler
|
||||
promoteHandler promote.Handler
|
||||
flaggerHandler flagger.Handler
|
||||
dashboardModule dashboard.Module
|
||||
dashboardHandler dashboard.Handler
|
||||
metricsExplorerHandler metricsexplorer.Handler
|
||||
gatewayHandler gateway.Handler
|
||||
fieldsHandler fields.Handler
|
||||
authzHandler authz.Handler
|
||||
rawDataExportHandler rawdataexport.Handler
|
||||
zeusHandler zeus.Handler
|
||||
querierHandler querier.Handler
|
||||
serviceAccountHandler serviceaccount.Handler
|
||||
factoryHandler factory.Handler
|
||||
cloudIntegrationHandler cloudintegration.Handler
|
||||
config apiserver.Config
|
||||
settings factory.ScopedProviderSettings
|
||||
router *mux.Router
|
||||
authZ *middleware.AuthZ
|
||||
orgHandler organization.Handler
|
||||
userHandler user.Handler
|
||||
sessionHandler session.Handler
|
||||
authDomainHandler authdomain.Handler
|
||||
preferenceHandler preference.Handler
|
||||
globalHandler global.Handler
|
||||
promoteHandler promote.Handler
|
||||
flaggerHandler flagger.Handler
|
||||
dashboardModule dashboard.Module
|
||||
dashboardHandler dashboard.Handler
|
||||
metricsExplorerHandler metricsexplorer.Handler
|
||||
gatewayHandler gateway.Handler
|
||||
fieldsHandler fields.Handler
|
||||
authzHandler authz.Handler
|
||||
rawDataExportHandler rawdataexport.Handler
|
||||
zeusHandler zeus.Handler
|
||||
querierHandler querier.Handler
|
||||
serviceAccountHandler serviceaccount.Handler
|
||||
factoryHandler factory.Handler
|
||||
}
|
||||
|
||||
func NewFactory(
|
||||
@@ -79,7 +77,6 @@ func NewFactory(
|
||||
querierHandler querier.Handler,
|
||||
serviceAccountHandler serviceaccount.Handler,
|
||||
factoryHandler factory.Handler,
|
||||
cloudIntegrationHandler cloudintegration.Handler,
|
||||
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
||||
return newProvider(
|
||||
@@ -107,7 +104,6 @@ func NewFactory(
|
||||
querierHandler,
|
||||
serviceAccountHandler,
|
||||
factoryHandler,
|
||||
cloudIntegrationHandler,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -137,35 +133,33 @@ func newProvider(
|
||||
querierHandler querier.Handler,
|
||||
serviceAccountHandler serviceaccount.Handler,
|
||||
factoryHandler factory.Handler,
|
||||
cloudIntegrationHandler cloudintegration.Handler,
|
||||
) (apiserver.APIServer, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
||||
router := mux.NewRouter().UseEncodedPath()
|
||||
|
||||
provider := &provider{
|
||||
config: config,
|
||||
settings: settings,
|
||||
router: router,
|
||||
orgHandler: orgHandler,
|
||||
userHandler: userHandler,
|
||||
sessionHandler: sessionHandler,
|
||||
authDomainHandler: authDomainHandler,
|
||||
preferenceHandler: preferenceHandler,
|
||||
globalHandler: globalHandler,
|
||||
promoteHandler: promoteHandler,
|
||||
flaggerHandler: flaggerHandler,
|
||||
dashboardModule: dashboardModule,
|
||||
dashboardHandler: dashboardHandler,
|
||||
metricsExplorerHandler: metricsExplorerHandler,
|
||||
gatewayHandler: gatewayHandler,
|
||||
fieldsHandler: fieldsHandler,
|
||||
authzHandler: authzHandler,
|
||||
rawDataExportHandler: rawDataExportHandler,
|
||||
zeusHandler: zeusHandler,
|
||||
querierHandler: querierHandler,
|
||||
serviceAccountHandler: serviceAccountHandler,
|
||||
factoryHandler: factoryHandler,
|
||||
cloudIntegrationHandler: cloudIntegrationHandler,
|
||||
config: config,
|
||||
settings: settings,
|
||||
router: router,
|
||||
orgHandler: orgHandler,
|
||||
userHandler: userHandler,
|
||||
sessionHandler: sessionHandler,
|
||||
authDomainHandler: authDomainHandler,
|
||||
preferenceHandler: preferenceHandler,
|
||||
globalHandler: globalHandler,
|
||||
promoteHandler: promoteHandler,
|
||||
flaggerHandler: flaggerHandler,
|
||||
dashboardModule: dashboardModule,
|
||||
dashboardHandler: dashboardHandler,
|
||||
metricsExplorerHandler: metricsExplorerHandler,
|
||||
gatewayHandler: gatewayHandler,
|
||||
fieldsHandler: fieldsHandler,
|
||||
authzHandler: authzHandler,
|
||||
rawDataExportHandler: rawDataExportHandler,
|
||||
zeusHandler: zeusHandler,
|
||||
querierHandler: querierHandler,
|
||||
serviceAccountHandler: serviceAccountHandler,
|
||||
factoryHandler: factoryHandler,
|
||||
}
|
||||
|
||||
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
|
||||
@@ -258,10 +252,6 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := provider.addCloudIntegrationRoutes(router); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user", handler.New(provider.authZ.AdminAccess(provider.userHandler.ListUsers), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/user", handler.New(provider.authZ.AdminAccess(provider.userHandler.ListUsersDeprecated), handler.OpenAPIDef{
|
||||
ID: "ListUsers",
|
||||
Tags: []string{"users"},
|
||||
Summary: "List users",
|
||||
@@ -128,7 +128,24 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user/me", handler.New(provider.authZ.OpenAccess(provider.userHandler.GetMyUser), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v2/users", handler.New(provider.authZ.AdminAccess(provider.userHandler.ListUsers), handler.OpenAPIDef{
|
||||
ID: "ListUsersV2",
|
||||
Tags: []string{"users"},
|
||||
Summary: "List users v2",
|
||||
Description: "This endpoint lists all users for the organization",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: make([]*types.User, 0),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user/me", handler.New(provider.authZ.OpenAccess(provider.userHandler.GetMyUserDeprecated), handler.OpenAPIDef{
|
||||
ID: "GetMyUser",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get my user",
|
||||
@@ -145,7 +162,41 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user/{id}", handler.New(provider.authZ.SelfAccess(provider.userHandler.GetUser), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v2/users/me", handler.New(provider.authZ.OpenAccess(provider.userHandler.GetMyUser), handler.OpenAPIDef{
|
||||
ID: "GetMyUserV2",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get my user v2",
|
||||
Description: "This endpoint returns the user I belong to",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(authtypes.UserWithRoles),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: []handler.OpenAPISecurityScheme{{Name: authtypes.IdentNProviderTokenizer.StringValue()}},
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/me", handler.New(provider.authZ.OpenAccess(provider.userHandler.UpdateMyUser), handler.OpenAPIDef{
|
||||
ID: "UpdateMyUserV2",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Update my user v2",
|
||||
Description: "This endpoint updates the user I belong to",
|
||||
Request: new(types.UpdatableSelfUser),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: []handler.OpenAPISecurityScheme{{Name: authtypes.IdentNProviderTokenizer.StringValue()}},
|
||||
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user/{id}", handler.New(provider.authZ.SelfAccess(provider.userHandler.GetUserDeprecated), handler.OpenAPIDef{
|
||||
ID: "GetUser",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get user",
|
||||
@@ -162,7 +213,24 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user/{id}", handler.New(provider.authZ.SelfAccess(provider.userHandler.UpdateUser), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v2/users/{id}", handler.New(provider.authZ.AdminAccess(provider.userHandler.GetUser), handler.OpenAPIDef{
|
||||
ID: "GetUserV2",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get user by user id",
|
||||
Description: "This endpoint returns the user by id",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: new(types.User),
|
||||
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/v1/user/{id}", handler.New(provider.authZ.SelfAccess(provider.userHandler.UpdateUserDeprecated), handler.OpenAPIDef{
|
||||
ID: "UpdateUser",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Update user",
|
||||
@@ -179,6 +247,23 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/{id}", handler.New(provider.authZ.AdminAccess(provider.userHandler.UpdateUser), handler.OpenAPIDef{
|
||||
ID: "UpdateUserV2",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Update user v2",
|
||||
Description: "This endpoint updates the user by id",
|
||||
Request: new(types.UpdatableUser),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user/{id}", handler.New(provider.authZ.AdminAccess(provider.userHandler.DeleteUser), handler.OpenAPIDef{
|
||||
ID: "DeleteUser",
|
||||
Tags: []string{"users"},
|
||||
@@ -264,5 +349,39 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/users/{id}/roles", handler.New(provider.authZ.AdminAccess(provider.userHandler.GetUserRoles), handler.OpenAPIDef{
|
||||
ID: "GetUserRoles",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get user roles",
|
||||
Description: "This endpoint returns the user roles by user id",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: make([]*authtypes.Role, 0),
|
||||
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/roles/{id}/users", handler.New(provider.authZ.AdminAccess(provider.userHandler.GetUsersByRoleID), handler.OpenAPIDef{
|
||||
ID: "GetUsersByRoleID",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Get users by role id",
|
||||
Description: "This endpoint returns the users having the role by role id",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: make([]*types.User, 0),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ type Module interface {
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
CreateAccount(http.ResponseWriter, *http.Request)
|
||||
GetConnectionArtifact(http.ResponseWriter, *http.Request)
|
||||
ListAccounts(http.ResponseWriter, *http.Request)
|
||||
GetAccount(http.ResponseWriter, *http.Request)
|
||||
UpdateAccount(http.ResponseWriter, *http.Request)
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package implcloudintegration
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
)
|
||||
|
||||
type handler struct{}
|
||||
|
||||
func NewHandler() cloudintegration.Handler {
|
||||
return &handler{}
|
||||
}
|
||||
|
||||
func (handler *handler) CreateAccount(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) ListAccounts(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) GetAccount(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) UpdateAccount(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) DisconnectAccount(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) ListServicesMetadata(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) GetService(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) UpdateService(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (handler *handler) AgentCheckIn(writer http.ResponseWriter, request *http.Request) {
|
||||
// TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func (module *getter) GetRootUserByOrgID(ctx context.Context, orgID valuer.UUID)
|
||||
return rootUser, userRoles, nil
|
||||
}
|
||||
|
||||
func (module *getter) ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.DeprecatedUser, error) {
|
||||
func (module *getter) ListByOrgIDDeprecated(ctx context.Context, orgID valuer.UUID) ([]*types.DeprecatedUser, error) {
|
||||
users, err := module.store.ListUsersByOrgID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -84,6 +84,23 @@ func (module *getter) ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*ty
|
||||
return deprecatedUsers, nil
|
||||
}
|
||||
|
||||
func (module *getter) ListByOrgID(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) GetDeprecatedUserByOrgIDAndID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.DeprecatedUser, error) {
|
||||
user, err := module.store.GetByOrgIDAndID(ctx, orgID, id)
|
||||
if err != nil {
|
||||
@@ -99,11 +116,19 @@ func (module *getter) GetDeprecatedUserByOrgIDAndID(ctx context.Context, orgID v
|
||||
return nil, errors.New(errors.TypeUnexpected, authtypes.ErrCodeUserRolesNotFound, "no user roles entries found")
|
||||
}
|
||||
|
||||
if userRoles[0].Role == nil {
|
||||
return nil, errors.New(errors.TypeUnexpected, authtypes.ErrCodeRoleNotFound, "role not found for user role entry")
|
||||
}
|
||||
|
||||
role := authtypes.SigNozManagedRoleToExistingLegacyRole[userRoles[0].Role.Name]
|
||||
|
||||
return types.NewDeprecatedUserFromUserAndRole(user, role), 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) Get(ctx context.Context, id valuer.UUID) (*types.DeprecatedUser, error) {
|
||||
user, err := module.store.GetUser(ctx, id)
|
||||
if err != nil {
|
||||
@@ -119,6 +144,10 @@ func (module *getter) Get(ctx context.Context, id valuer.UUID) (*types.Deprecate
|
||||
return nil, errors.New(errors.TypeUnexpected, authtypes.ErrCodeUserRolesNotFound, "no user roles entries found")
|
||||
}
|
||||
|
||||
if userRoles[0].Role == nil {
|
||||
return nil, errors.New(errors.TypeUnexpected, authtypes.ErrCodeRoleNotFound, "role not found for user role entry")
|
||||
}
|
||||
|
||||
role := authtypes.SigNozManagedRoleToExistingLegacyRole[userRoles[0].Role.Name]
|
||||
|
||||
return types.NewDeprecatedUserFromUserAndRole(user, role), nil
|
||||
@@ -180,5 +209,15 @@ func (module *getter) GetUserRoles(ctx context.Context, userID valuer.UUID) ([]*
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ur := range userRoles {
|
||||
if ur.Role == nil {
|
||||
return nil, errors.New(errors.TypeUnexpected, authtypes.ErrCodeRoleNotFound, "role not found for user role entry")
|
||||
}
|
||||
}
|
||||
|
||||
return userRoles, nil
|
||||
}
|
||||
|
||||
func (module *getter) GetUsersByOrgIDAndRoleID(ctx context.Context, orgID valuer.UUID, roleID valuer.UUID) ([]*types.User, error) {
|
||||
return module.store.GetUsersByOrgIDAndRoleID(ctx, orgID, roleID)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func (h *handler) CreateBulkInvite(rw http.ResponseWriter, r *http.Request) {
|
||||
render.Success(rw, http.StatusCreated, nil)
|
||||
}
|
||||
|
||||
func (h *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *handler) GetUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -106,7 +106,28 @@ func (h *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (h *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *handler) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
userID := mux.Vars(r)["id"]
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (h *handler) GetMyUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -125,6 +146,85 @@ func (h *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (h *handler) GetMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
userRoles, err := h.getter.GetUserRoles(ctx, user.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
roles := make([]*authtypes.Role, len(userRoles))
|
||||
for idx, userRole := range userRoles {
|
||||
roles[idx] = authtypes.NewRoleFromStorableRole(userRole.Role)
|
||||
}
|
||||
|
||||
userWithRoles := &authtypes.UserWithRoles{
|
||||
User: user,
|
||||
Roles: roles,
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, userWithRoles)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateMyUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
updatableSelfUser := new(types.UpdatableSelfUser)
|
||||
if err := json.NewDecoder(r.Body).Decode(&updatableSelfUser); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.setter.UpdateMyUser(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(claims.UserID), updatableSelfUser)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) ListUsersDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := h.getter.ListByOrgIDDeprecated(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
func (h *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -144,7 +244,7 @@ func (h *handler) ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *handler) UpdateUserDeprecated(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -162,7 +262,7 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
updatedUser, err := h.setter.UpdateUser(ctx, valuer.MustNewUUID(claims.OrgID), id, &user, claims.UserID)
|
||||
updatedUser, err := h.setter.UpdateUserDeprecated(ctx, valuer.MustNewUUID(claims.OrgID), id, &user, claims.UserID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
@@ -171,6 +271,33 @@ func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.Success(w, http.StatusOK, updatedUser)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
userID := mux.Vars(r)["id"]
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
updatableUser := new(types.UpdatableUser)
|
||||
if err := json.NewDecoder(r.Body).Decode(&updatableUser); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.setter.UpdateUser(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID), updatableUser, valuer.MustNewUUID(claims.UserID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
@@ -443,3 +570,56 @@ func (h *handler) RevokeAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) GetUserRoles(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
userID := mux.Vars(r)["id"]
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.getter.GetUserByOrgIDAndID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(userID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
userRoles, err := h.getter.GetUserRoles(ctx, user.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
roles := make([]*authtypes.Role, len(userRoles))
|
||||
for idx, userRole := range userRoles {
|
||||
roles[idx] = authtypes.NewRoleFromStorableRole(userRole.Role)
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, roles)
|
||||
}
|
||||
|
||||
func (h *handler) GetUsersByRoleID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
roleID := mux.Vars(r)["id"]
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := h.getter.GetUsersByOrgIDAndRoleID(ctx, valuer.MustNewUUID(claims.OrgID), valuer.MustNewUUID(roleID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, users)
|
||||
}
|
||||
|
||||
@@ -156,9 +156,7 @@ func (s *service) createOrPromoteRootUser(ctx context.Context, orgID valuer.UUID
|
||||
existingUser.PromoteToRoot()
|
||||
|
||||
err = s.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||
// update users table
|
||||
deprecatedUser := types.NewDeprecatedUserFromUserAndRole(existingUser, types.RoleAdmin)
|
||||
if err := s.setter.UpdateAnyUser(ctx, orgID, deprecatedUser); err != nil {
|
||||
if err := s.setter.UpdateAnyUser(ctx, orgID, existingUser); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -201,8 +199,7 @@ func (s *service) updateExistingRootUser(ctx context.Context, orgID valuer.UUID,
|
||||
|
||||
if existingRoot.Email != s.config.Email {
|
||||
existingRoot.UpdateEmail(s.config.Email)
|
||||
deprecatedUser := types.NewDeprecatedUserFromUserAndRole(existingRoot, types.RoleAdmin)
|
||||
if err := s.setter.UpdateAnyUser(ctx, orgID, deprecatedUser); err != nil {
|
||||
if err := s.setter.UpdateAnyUser(ctx, orgID, existingRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ func (module *setter) CreateUser(ctx context.Context, user *types.User, opts ...
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *setter) UpdateUser(ctx context.Context, orgID valuer.UUID, id string, user *types.DeprecatedUser, updatedBy string) (*types.DeprecatedUser, error) {
|
||||
func (module *setter) UpdateUserDeprecated(ctx context.Context, orgID valuer.UUID, id string, user *types.DeprecatedUser, updatedBy string) (*types.DeprecatedUser, error) {
|
||||
existingUser, err := module.getter.GetDeprecatedUserByOrgIDAndID(ctx, orgID, valuer.MustNewUUID(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -265,7 +265,7 @@ func (module *setter) UpdateUser(ctx context.Context, orgID valuer.UUID, id stri
|
||||
existingUser.Update(user.DisplayName, user.Role)
|
||||
|
||||
// update the user - idempotent (this does analytics too so keeping it outside txn)
|
||||
if err := module.UpdateAnyUser(ctx, orgID, existingUser); err != nil {
|
||||
if err := module.UpdateAnyUserDeprecated(ctx, orgID, existingUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -291,7 +291,113 @@ func (module *setter) UpdateUser(ctx context.Context, orgID valuer.UUID, id stri
|
||||
return existingUser, nil
|
||||
}
|
||||
|
||||
func (module *setter) UpdateAnyUser(ctx context.Context, orgID valuer.UUID, deprecateUser *types.DeprecatedUser) error {
|
||||
func (module *setter) UpdateMyUser(ctx context.Context, orgID valuer.UUID, userID valuer.UUID, updatable *types.UpdatableSelfUser) (*types.User, error) {
|
||||
existingUser, err := module.getter.GetUserByOrgIDAndID(ctx, orgID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := existingUser.ErrIfRoot(); err != nil {
|
||||
return nil, errors.WithAdditionalf(err, "cannot update root user")
|
||||
}
|
||||
|
||||
if err := existingUser.ErrIfDeleted(); err != nil {
|
||||
return nil, errors.WithAdditionalf(err, "cannot update deleted user")
|
||||
}
|
||||
|
||||
existingUser.Update(updatable.DisplayName)
|
||||
if err := module.UpdateAnyUser(ctx, orgID, existingUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return existingUser, nil
|
||||
}
|
||||
|
||||
func (module *setter) UpdateUser(ctx context.Context, orgID valuer.UUID, userID valuer.UUID, updatable *types.UpdatableUser, updatedBy valuer.UUID) (*types.User, error) {
|
||||
existingUser, err := module.getter.GetUserByOrgIDAndID(ctx, orgID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := existingUser.ErrIfRoot(); err != nil {
|
||||
return nil, errors.WithAdditionalf(err, "cannot update root user")
|
||||
}
|
||||
|
||||
if err := existingUser.ErrIfDeleted(); err != nil {
|
||||
return nil, errors.WithAdditionalf(err, "cannot update deleted user")
|
||||
}
|
||||
|
||||
existingUserRoles, err := module.getter.GetUserRoles(ctx, existingUser.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existingUserRoleNames := roleNamesFromUserRoles(existingUserRoles)
|
||||
|
||||
var grants, revokes []string
|
||||
var rolesChanged bool
|
||||
if len(updatable.RoleNames) > 0 {
|
||||
// validate that all requested role names exist before making any changes
|
||||
_, err := module.authz.ListByOrgIDAndNames(ctx, orgID, updatable.RoleNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
grants, revokes = module.patchRolesNames(existingUserRoleNames, updatable.RoleNames)
|
||||
rolesChanged = (len(grants) > 0) || (len(revokes) > 0)
|
||||
|
||||
if rolesChanged && existingUser.ID == updatedBy {
|
||||
return nil, errors.New(errors.TypeForbidden, errors.CodeForbidden, "cannot change self roles")
|
||||
}
|
||||
|
||||
if rolesChanged {
|
||||
err = module.authz.ModifyGrant(
|
||||
ctx,
|
||||
orgID,
|
||||
revokes,
|
||||
grants,
|
||||
authtypes.MustNewSubject(authtypes.TypeableUser, userID.String(), orgID, nil),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
existingUser.Update(updatable.DisplayName)
|
||||
|
||||
if err := module.UpdateAnyUser(ctx, existingUser.OrgID, existingUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rolesChanged {
|
||||
// this by default runs in txn
|
||||
if err := module.UpdateUserRoles(ctx, existingUser.OrgID, existingUser.ID, updatable.RoleNames); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return existingUser, nil
|
||||
}
|
||||
|
||||
func (module *setter) UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user *types.User) error {
|
||||
if err := module.store.UpdateUser(ctx, orgID, user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := module.tokenizer.DeleteIdentity(ctx, user.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// stats collector things
|
||||
traits := types.NewTraitsFromUser(user)
|
||||
module.analytics.IdentifyUser(ctx, user.OrgID.String(), user.ID.String(), traits)
|
||||
module.analytics.TrackUser(ctx, user.OrgID.String(), user.ID.String(), "User Updated", traits)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *setter) UpdateAnyUserDeprecated(ctx context.Context, orgID valuer.UUID, deprecateUser *types.DeprecatedUser) error {
|
||||
user := types.NewUserFromDeprecatedUser(deprecateUser)
|
||||
if err := module.store.UpdateUser(ctx, orgID, user); err != nil {
|
||||
return err
|
||||
@@ -801,13 +907,17 @@ func (module *setter) activatePendingUser(ctx context.Context, user *types.User,
|
||||
|
||||
func (module *setter) UpdateUserRoles(ctx context.Context, orgID, userID valuer.UUID, finalRoleNames []string) error {
|
||||
return module.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||
// delete old user_role entries and create new ones from SSO
|
||||
// delete old user_role entries
|
||||
if err := module.userRoleStore.DeleteUserRoles(ctx, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create fresh ones
|
||||
return module.createUserRoleEntries(ctx, orgID, userID, finalRoleNames)
|
||||
// create fresh ones only if there are roles to assign
|
||||
if len(finalRoleNames) > 0 {
|
||||
return module.createUserRoleEntries(ctx, orgID, userID, finalRoleNames)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -820,3 +930,33 @@ func roleNamesFromUserRoles(userRoles []*authtypes.UserRole) []string {
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func (module *setter) patchRolesNames(currentRolesNames, targetRoleNames []string) ([]string, []string) {
|
||||
currentRolesSet := make(map[string]struct{}, len(currentRolesNames))
|
||||
targetRolesSet := make(map[string]struct{}, len(targetRoleNames))
|
||||
|
||||
for _, role := range currentRolesNames {
|
||||
currentRolesSet[role] = struct{}{}
|
||||
}
|
||||
for _, role := range targetRoleNames {
|
||||
targetRolesSet[role] = struct{}{}
|
||||
}
|
||||
|
||||
// additions: roles present in input but not in current
|
||||
additions := []string{}
|
||||
for _, role := range targetRoleNames {
|
||||
if _, exists := currentRolesSet[role]; !exists {
|
||||
additions = append(additions, role)
|
||||
}
|
||||
}
|
||||
|
||||
// deletions: roles present in current but not in input
|
||||
deletions := []string{}
|
||||
for _, role := range currentRolesNames {
|
||||
if _, exists := targetRolesSet[role]; !exists {
|
||||
deletions = append(deletions, role)
|
||||
}
|
||||
}
|
||||
|
||||
return additions, deletions
|
||||
}
|
||||
|
||||
@@ -667,3 +667,22 @@ func (store *store) GetUsersByEmailsOrgIDAndStatuses(ctx context.Context, orgID
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (store *store) GetUsersByOrgIDAndRoleID(ctx context.Context, orgID valuer.UUID, roleID valuer.UUID) ([]*types.User, error) {
|
||||
users := []*types.User{}
|
||||
|
||||
err := store.
|
||||
sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(&users).
|
||||
Join(`JOIN user_role ON user_role.user_id = "users".id`).
|
||||
Where(`"users".org_id = ?`, orgID).
|
||||
Where("user_role.role_id = ?", roleID).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
@@ -34,10 +34,13 @@ type Setter interface {
|
||||
// Initiate forgot password flow for a user
|
||||
ForgotPassword(ctx context.Context, orgID valuer.UUID, email valuer.Email, frontendBaseURL string) error
|
||||
|
||||
UpdateUser(ctx context.Context, orgID valuer.UUID, id string, user *types.DeprecatedUser, updatedBy string) (*types.DeprecatedUser, error)
|
||||
UpdateUserDeprecated(ctx context.Context, orgID valuer.UUID, id string, user *types.DeprecatedUser, updatedBy string) (*types.DeprecatedUser, error)
|
||||
UpdateMyUser(ctx context.Context, orgID valuer.UUID, userID valuer.UUID, updatable *types.UpdatableSelfUser) (*types.User, error)
|
||||
UpdateUser(ctx context.Context, orgID valuer.UUID, userID valuer.UUID, updatable *types.UpdatableUser, updatedBy valuer.UUID) (*types.User, error)
|
||||
|
||||
// UpdateAnyUser updates a user and persists the changes to the database along with the analytics and identity deletion.
|
||||
UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user *types.DeprecatedUser) error
|
||||
UpdateAnyUserDeprecated(ctx context.Context, orgID valuer.UUID, deprecateUser *types.DeprecatedUser) error
|
||||
UpdateAnyUser(ctx context.Context, orgID valuer.UUID, user *types.User) error
|
||||
DeleteUser(ctx context.Context, orgID valuer.UUID, id string, deletedBy string) error
|
||||
|
||||
// invite
|
||||
@@ -60,11 +63,13 @@ type Getter interface {
|
||||
// Get root user by org id.
|
||||
GetRootUserByOrgID(context.Context, valuer.UUID) (*types.User, []*authtypes.UserRole, error)
|
||||
|
||||
// Get gets the users based on the given id
|
||||
ListByOrgID(context.Context, valuer.UUID) ([]*types.DeprecatedUser, error)
|
||||
// Get gets the users based on the given org id
|
||||
ListByOrgIDDeprecated(context.Context, valuer.UUID) ([]*types.DeprecatedUser, error)
|
||||
ListByOrgID(ctx context.Context, orgID valuer.UUID) ([]*types.User, error)
|
||||
|
||||
// Get deprecated user object by orgID and id.
|
||||
GetDeprecatedUserByOrgIDAndID(context.Context, valuer.UUID, valuer.UUID) (*types.DeprecatedUser, error)
|
||||
GetUserByOrgIDAndID(ctx context.Context, orgID valuer.UUID, userID valuer.UUID) (*types.User, error)
|
||||
|
||||
// Get user by id.
|
||||
Get(context.Context, valuer.UUID) (*types.DeprecatedUser, error)
|
||||
@@ -86,6 +91,9 @@ type Getter interface {
|
||||
|
||||
// Gets user_role with roles entries from db
|
||||
GetUserRoles(ctx context.Context, userID valuer.UUID) ([]*authtypes.UserRole, error)
|
||||
|
||||
// Gets all the user with role using role id in an org id
|
||||
GetUsersByOrgIDAndRoleID(ctx context.Context, orgID valuer.UUID, roleID valuer.UUID) ([]*types.User, error)
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
@@ -93,11 +101,19 @@ type Handler interface {
|
||||
CreateInvite(http.ResponseWriter, *http.Request)
|
||||
CreateBulkInvite(http.ResponseWriter, *http.Request)
|
||||
|
||||
// users
|
||||
ListUsersDeprecated(http.ResponseWriter, *http.Request)
|
||||
ListUsers(http.ResponseWriter, *http.Request)
|
||||
UpdateUserDeprecated(http.ResponseWriter, *http.Request)
|
||||
UpdateUser(http.ResponseWriter, *http.Request)
|
||||
DeleteUser(http.ResponseWriter, *http.Request)
|
||||
GetUserDeprecated(http.ResponseWriter, *http.Request)
|
||||
GetUser(http.ResponseWriter, *http.Request)
|
||||
GetMyUserDeprecated(http.ResponseWriter, *http.Request)
|
||||
GetMyUser(http.ResponseWriter, *http.Request)
|
||||
UpdateMyUser(http.ResponseWriter, *http.Request)
|
||||
GetUserRoles(http.ResponseWriter, *http.Request)
|
||||
GetUsersByRoleID(http.ResponseWriter, *http.Request)
|
||||
|
||||
// Reset Password
|
||||
GetResetPasswordToken(http.ResponseWriter, *http.Request)
|
||||
|
||||
@@ -12,8 +12,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/modules/apdex"
|
||||
"github.com/SigNoz/signoz/pkg/modules/apdex/implapdex"
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration/implcloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/fields"
|
||||
@@ -40,25 +38,24 @@ import (
|
||||
)
|
||||
|
||||
type Handlers struct {
|
||||
SavedView savedview.Handler
|
||||
Apdex apdex.Handler
|
||||
Dashboard dashboard.Handler
|
||||
QuickFilter quickfilter.Handler
|
||||
TraceFunnel tracefunnel.Handler
|
||||
RawDataExport rawdataexport.Handler
|
||||
SpanPercentile spanpercentile.Handler
|
||||
Services services.Handler
|
||||
MetricsExplorer metricsexplorer.Handler
|
||||
Global global.Handler
|
||||
FlaggerHandler flagger.Handler
|
||||
GatewayHandler gateway.Handler
|
||||
Fields fields.Handler
|
||||
AuthzHandler authz.Handler
|
||||
ZeusHandler zeus.Handler
|
||||
QuerierHandler querier.Handler
|
||||
ServiceAccountHandler serviceaccount.Handler
|
||||
RegistryHandler factory.Handler
|
||||
CloudIntegrationHandler cloudintegration.Handler
|
||||
SavedView savedview.Handler
|
||||
Apdex apdex.Handler
|
||||
Dashboard dashboard.Handler
|
||||
QuickFilter quickfilter.Handler
|
||||
TraceFunnel tracefunnel.Handler
|
||||
RawDataExport rawdataexport.Handler
|
||||
SpanPercentile spanpercentile.Handler
|
||||
Services services.Handler
|
||||
MetricsExplorer metricsexplorer.Handler
|
||||
Global global.Handler
|
||||
FlaggerHandler flagger.Handler
|
||||
GatewayHandler gateway.Handler
|
||||
Fields fields.Handler
|
||||
AuthzHandler authz.Handler
|
||||
ZeusHandler zeus.Handler
|
||||
QuerierHandler querier.Handler
|
||||
ServiceAccountHandler serviceaccount.Handler
|
||||
RegistryHandler factory.Handler
|
||||
}
|
||||
|
||||
func NewHandlers(
|
||||
@@ -76,24 +73,23 @@ func NewHandlers(
|
||||
registryHandler factory.Handler,
|
||||
) Handlers {
|
||||
return Handlers{
|
||||
SavedView: implsavedview.NewHandler(modules.SavedView),
|
||||
Apdex: implapdex.NewHandler(modules.Apdex),
|
||||
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings),
|
||||
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
|
||||
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
|
||||
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
|
||||
Services: implservices.NewHandler(modules.Services),
|
||||
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
|
||||
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
|
||||
Global: signozglobal.NewHandler(global),
|
||||
FlaggerHandler: flagger.NewHandler(flaggerService),
|
||||
GatewayHandler: gateway.NewHandler(gatewayService),
|
||||
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
|
||||
AuthzHandler: signozauthzapi.NewHandler(authz),
|
||||
ZeusHandler: zeus.NewHandler(zeusService, licensing),
|
||||
QuerierHandler: querierHandler,
|
||||
ServiceAccountHandler: implserviceaccount.NewHandler(modules.ServiceAccount),
|
||||
RegistryHandler: registryHandler,
|
||||
CloudIntegrationHandler: implcloudintegration.NewHandler(),
|
||||
SavedView: implsavedview.NewHandler(modules.SavedView),
|
||||
Apdex: implapdex.NewHandler(modules.Apdex),
|
||||
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings),
|
||||
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
|
||||
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
|
||||
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
|
||||
Services: implservices.NewHandler(modules.Services),
|
||||
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
|
||||
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
|
||||
Global: signozglobal.NewHandler(global),
|
||||
FlaggerHandler: flagger.NewHandler(flaggerService),
|
||||
GatewayHandler: gateway.NewHandler(gatewayService),
|
||||
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
|
||||
AuthzHandler: signozauthzapi.NewHandler(authz),
|
||||
ZeusHandler: zeus.NewHandler(zeusService, licensing),
|
||||
QuerierHandler: querierHandler,
|
||||
ServiceAccountHandler: implserviceaccount.NewHandler(modules.ServiceAccount),
|
||||
RegistryHandler: registryHandler,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||
"github.com/SigNoz/signoz/pkg/modules/authdomain"
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/fields"
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
|
||||
@@ -66,7 +65,6 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
|
||||
struct{ querier.Handler }{},
|
||||
struct{ serviceaccount.Handler }{},
|
||||
struct{ factory.Handler }{},
|
||||
struct{ cloudintegration.Handler }{},
|
||||
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -279,7 +279,6 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
|
||||
handlers.QuerierHandler,
|
||||
handlers.ServiceAccountHandler,
|
||||
handlers.RegistryHandler,
|
||||
handlers.CloudIntegrationHandler,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ func (provider *provider) Report(ctx context.Context) error {
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
traits := types.NewTraitsFromDeprecatedUser(user)
|
||||
traits := types.NewTraitsFromUser(user)
|
||||
if maxLastObservedAt, ok := maxLastObservedAtPerUserID[user.ID]; ok {
|
||||
traits["auth_token.last_observed_at.max.time"] = maxLastObservedAt.UTC()
|
||||
traits["auth_token.last_observed_at.max.time_unix"] = maxLastObservedAt.Unix()
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCodeUserRoleAlreadyExists = errors.MustNewCode("user_role_already_exists")
|
||||
ErrCodeUserRolesNotFound = errors.MustNewCode("user_roles_not_found")
|
||||
ErrCodeUserRolesNotFound = errors.MustNewCode("user_roles_not_found")
|
||||
)
|
||||
|
||||
type UserRole struct {
|
||||
@@ -47,6 +48,11 @@ func NewUserRoles(userID valuer.UUID, roles []*Role) []*UserRole {
|
||||
return userRoles
|
||||
}
|
||||
|
||||
type UserWithRoles struct {
|
||||
*types.User
|
||||
Roles []*Role `json:"roles"`
|
||||
}
|
||||
|
||||
type UserRoleStore interface {
|
||||
// create user roles in bulk
|
||||
CreateUserRoles(ctx context.Context, userRoles []*UserRole) error
|
||||
|
||||
@@ -10,35 +10,34 @@ import (
|
||||
type Account struct {
|
||||
types.Identifiable
|
||||
types.TimeAuditable
|
||||
ProviderAccountID *string `json:"providerAccountId" required:"true" nullable:"true"`
|
||||
Provider CloudProviderType `json:"provider" required:"true"`
|
||||
RemovedAt *time.Time `json:"removedAt" required:"true" nullable:"true"`
|
||||
AgentReport *AgentReport `json:"agentReport" required:"true" nullable:"true"`
|
||||
OrgID valuer.UUID `json:"orgId" required:"true"`
|
||||
Config *AccountConfig `json:"config" required:"true" nullable:"false"`
|
||||
ProviderAccountId *string `json:"providerAccountID,omitempty"`
|
||||
Provider CloudProviderType `json:"provider"`
|
||||
RemovedAt *time.Time `json:"removedAt,omitempty"`
|
||||
AgentReport *AgentReport `json:"agentReport,omitempty"`
|
||||
OrgID valuer.UUID `json:"orgID"`
|
||||
Config *AccountConfig `json:"config,omitempty"`
|
||||
}
|
||||
|
||||
// AgentReport represents heartbeats sent by the agent.
|
||||
type AgentReport struct {
|
||||
TimestampMillis int64 `json:"timestampMillis" required:"true"`
|
||||
Data map[string]any `json:"data" required:"true" nullable:"true"`
|
||||
}
|
||||
|
||||
type AccountConfig struct {
|
||||
// required till new providers are added
|
||||
AWS *AWSAccountConfig `json:"aws" required:"true" nullable:"false"`
|
||||
TimestampMillis int64 `json:"timestampMillis"`
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
|
||||
type GettableAccounts struct {
|
||||
Accounts []*Account `json:"accounts" required:"true" nullable:"false"`
|
||||
Accounts []*Account `json:"accounts"`
|
||||
}
|
||||
|
||||
type GettableAccount = Account
|
||||
|
||||
type UpdatableAccount struct {
|
||||
Config *AccountConfig `json:"config" required:"true" nullable:"false"`
|
||||
Config *AccountConfig `json:"config"`
|
||||
}
|
||||
|
||||
type AccountConfig struct {
|
||||
AWS *AWSAccountConfig `json:"aws,omitempty"`
|
||||
}
|
||||
|
||||
type AWSAccountConfig struct {
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
Regions []string `json:"regions"`
|
||||
}
|
||||
|
||||
@@ -1,81 +1,88 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
import "github.com/SigNoz/signoz/pkg/types/integrationtypes"
|
||||
|
||||
type ConnectionArtifactRequest struct {
|
||||
// required till new providers are added
|
||||
Aws *AWSConnectionArtifactRequest `json:"aws" required:"true" nullable:"false"`
|
||||
Aws *AWSConnectionArtifactRequest `json:"aws"`
|
||||
}
|
||||
|
||||
type AWSConnectionArtifactRequest struct {
|
||||
DeploymentRegion string `json:"deploymentRegion" required:"true"`
|
||||
Regions []string `json:"regions" required:"true" nullable:"false"`
|
||||
DeploymentRegion string `json:"deploymentRegion"`
|
||||
Regions []string `json:"regions"`
|
||||
}
|
||||
|
||||
type PostableConnectionArtifact = ConnectionArtifactRequest
|
||||
|
||||
type ConnectionArtifact struct {
|
||||
// required till new providers are added
|
||||
Aws *AWSConnectionArtifact `json:"aws" required:"true" nullable:"false"`
|
||||
Aws *AWSConnectionArtifact `json:"aws"`
|
||||
}
|
||||
|
||||
type AWSConnectionArtifact struct {
|
||||
ConnectionURL string `json:"connectionURL" required:"true"`
|
||||
ConnectionUrl string `json:"connectionURL"`
|
||||
}
|
||||
|
||||
type GettableAccountWithArtifact struct {
|
||||
ID valuer.UUID `json:"id" required:"true"`
|
||||
Artifact *ConnectionArtifact `json:"connectionArtifact" required:"true"`
|
||||
type GettableConnectionArtifact = ConnectionArtifact
|
||||
|
||||
type AccountStatus struct {
|
||||
Id string `json:"id"`
|
||||
ProviderAccountId *string `json:"providerAccountID,omitempty"`
|
||||
Status integrationtypes.AccountStatus `json:"status"`
|
||||
}
|
||||
|
||||
type GettableAccountStatus = AccountStatus
|
||||
|
||||
type AgentCheckInRequest struct {
|
||||
ProviderAccountID string `json:"providerAccountId" required:"false"`
|
||||
CloudIntegrationID string `json:"cloudIntegrationId" required:"false"`
|
||||
// older backward compatible fields are mapped to new fields
|
||||
// CloudIntegrationId string `json:"cloudIntegrationId"`
|
||||
// AccountId string `json:"accountId"`
|
||||
|
||||
Data map[string]any `json:"data" required:"true" nullable:"true"`
|
||||
// New fields
|
||||
ProviderAccountId string `json:"providerAccountId"`
|
||||
CloudAccountId string `json:"cloudAccountId"`
|
||||
|
||||
Data map[string]any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type PostableAgentCheckInRequest struct {
|
||||
AgentCheckInRequest
|
||||
// following are backward compatible fields for older running agents
|
||||
// which gets mapped to new fields in AgentCheckInRequest
|
||||
ID string `json:"account_id" required:"false"` // => CloudIntegrationID
|
||||
AccountID string `json:"cloud_account_id" required:"false"` // => ProviderAccountID
|
||||
}
|
||||
|
||||
type AgentCheckInResponse struct {
|
||||
CloudIntegrationID string `json:"cloudIntegrationId" required:"true"`
|
||||
ProviderAccountID string `json:"providerAccountId" required:"true"`
|
||||
IntegrationConfig *ProviderIntegrationConfig `json:"integrationConfig" required:"true"`
|
||||
RemovedAt *time.Time `json:"removedAt" required:"true" nullable:"true"`
|
||||
CloudIntegrationId string `json:"cloud_integration_id"`
|
||||
CloudAccountId string `json:"cloud_account_id"`
|
||||
}
|
||||
|
||||
type GettableAgentCheckInResponse struct {
|
||||
// Older fields for backward compatibility with existing AWS agents
|
||||
AccountID string `json:"account_id" required:"true"`
|
||||
CloudAccountID string `json:"cloud_account_id" required:"true"`
|
||||
OlderIntegrationConfig *IntegrationConfig `json:"integration_config" required:"true" nullable:"true"`
|
||||
OlderRemovedAt *time.Time `json:"removed_at" required:"true" nullable:"true"`
|
||||
|
||||
AgentCheckInResponse
|
||||
|
||||
// For backward compatibility
|
||||
CloudIntegrationId string `json:"cloud_integration_id"`
|
||||
AccountId string `json:"account_id"`
|
||||
}
|
||||
|
||||
type AgentCheckInResponse struct {
|
||||
// Older fields for backward compatibility are mapped to new fields below
|
||||
// CloudIntegrationId string `json:"cloud_integration_id"`
|
||||
// AccountId string `json:"account_id"`
|
||||
|
||||
// New fields
|
||||
ProviderAccountId string `json:"providerAccountId"`
|
||||
CloudAccountId string `json:"cloudAccountId"`
|
||||
|
||||
// IntegrationConfig populates data related to integration that is required for an agent
|
||||
// to start collecting telemetry data
|
||||
// keeping JSON key snake_case for backward compatibility
|
||||
IntegrationConfig *IntegrationConfig `json:"integration_config,omitempty"`
|
||||
}
|
||||
|
||||
// IntegrationConfig older integration config struct for backward compatibility,
|
||||
// this will be eventually removed once agents are updated to use new struct.
|
||||
type IntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabled_regions" required:"true" nullable:"false"` // backward compatible
|
||||
Telemetry *AWSCollectionStrategy `json:"telemetry" required:"true" nullable:"false"` // backward compatible
|
||||
}
|
||||
EnabledRegions []string `json:"enabledRegions"` // backward compatible
|
||||
Telemetry *AWSCollectionStrategy `json:"telemetry,omitempty"` // backward compatible
|
||||
|
||||
type ProviderIntegrationConfig struct {
|
||||
AWS *AWSIntegrationConfig `json:"aws" required:"true" nullable:"false"`
|
||||
// new fields
|
||||
AWS *AWSIntegrationConfig `json:"aws,omitempty"`
|
||||
}
|
||||
|
||||
type AWSIntegrationConfig struct {
|
||||
EnabledRegions []string `json:"enabledRegions" required:"true" nullable:"false"`
|
||||
Telemetry *AWSCollectionStrategy `json:"telemetry" required:"true" nullable:"false"`
|
||||
EnabledRegions []string `json:"enabledRegions"`
|
||||
Telemetry *AWSCollectionStrategy `json:"telemetry,omitempty"`
|
||||
}
|
||||
|
||||
@@ -10,19 +10,20 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
var ErrCodeInvalidServiceID = errors.MustNewCode("invalid_service_id")
|
||||
var (
|
||||
S3Sync = valuer.NewString("s3sync")
|
||||
// ErrCodeInvalidServiceID is the error code for invalid service id.
|
||||
ErrCodeInvalidServiceID = errors.MustNewCode("invalid_service_id")
|
||||
)
|
||||
|
||||
type ServiceID struct{ valuer.String }
|
||||
|
||||
type CloudIntegrationService struct {
|
||||
types.Identifiable
|
||||
types.TimeAuditable
|
||||
Type ServiceID `json:"type"`
|
||||
Config *ServiceConfig `json:"config"`
|
||||
CloudIntegrationID valuer.UUID `json:"cloudIntegrationId"`
|
||||
}
|
||||
|
||||
type ServiceConfig struct {
|
||||
// required till new providers are added
|
||||
AWS *AWSServiceConfig `json:"aws" required:"true" nullable:"false"`
|
||||
CloudIntegrationID valuer.UUID `json:"cloudIntegrationID"`
|
||||
}
|
||||
|
||||
// ServiceMetadata helps to quickly list available services and whether it is enabled or not.
|
||||
@@ -31,56 +32,26 @@ type ServiceConfig struct {
|
||||
type ServiceMetadata struct {
|
||||
ServiceDefinitionMetadata
|
||||
// if the service is enabled for the account
|
||||
Enabled bool `json:"enabled" required:"true"`
|
||||
}
|
||||
|
||||
// ServiceDefinitionMetadata represents service definition metadata. This is useful for showing service tab in frontend.
|
||||
type ServiceDefinitionMetadata struct {
|
||||
ID string `json:"id" required:"true"`
|
||||
Title string `json:"title" required:"true"`
|
||||
Icon string `json:"icon" required:"true"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type GettableServicesMetadata struct {
|
||||
Services []*ServiceMetadata `json:"services" required:"true" nullable:"false"`
|
||||
Services []*ServiceMetadata `json:"services"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
ServiceDefinition
|
||||
ServiceConfig *ServiceConfig `json:"serviceConfig" required:"false" nullable:"false"`
|
||||
ServiceConfig *ServiceConfig `json:"serviceConfig"`
|
||||
}
|
||||
|
||||
type GettableService = Service
|
||||
|
||||
type UpdatableService struct {
|
||||
Config *ServiceConfig `json:"config" required:"true" nullable:"false"`
|
||||
Config *ServiceConfig `json:"config"`
|
||||
}
|
||||
|
||||
type ServiceDefinition struct {
|
||||
ServiceDefinitionMetadata
|
||||
Overview string `json:"overview" required:"true"` // markdown
|
||||
Assets Assets `json:"assets" required:"true"`
|
||||
SupportedSignals SupportedSignals `json:"supported_signals" required:"true"`
|
||||
DataCollected DataCollected `json:"dataCollected" required:"true"`
|
||||
Strategy *CollectionStrategy `json:"telemetryCollectionStrategy" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
// SupportedSignals for cloud provider's service.
|
||||
type SupportedSignals struct {
|
||||
Logs bool `json:"logs"`
|
||||
Metrics bool `json:"metrics"`
|
||||
}
|
||||
|
||||
// DataCollected is curated static list of metrics and logs, this is shown as part of service overview.
|
||||
type DataCollected struct {
|
||||
Logs []CollectedLogAttribute `json:"logs"`
|
||||
Metrics []CollectedMetric `json:"metrics"`
|
||||
}
|
||||
|
||||
// CollectionStrategy is cloud provider specific configuration for signal collection,
|
||||
// this is used by agent to understand the nitty-gritty for collecting telemetry for the cloud provider.
|
||||
type CollectionStrategy struct {
|
||||
AWS *AWSCollectionStrategy `json:"aws" required:"true" nullable:"false"`
|
||||
type ServiceConfig struct {
|
||||
AWS *AWSServiceConfig `json:"aws,omitempty"`
|
||||
}
|
||||
|
||||
type AWSServiceConfig struct {
|
||||
@@ -99,11 +70,45 @@ type AWSServiceMetricsConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// ServiceDefinitionMetadata represents service definition metadata. This is useful for showing service tab in frontend.
|
||||
type ServiceDefinitionMetadata struct {
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
type ServiceDefinition struct {
|
||||
ServiceDefinitionMetadata
|
||||
Overview string `json:"overview"` // markdown
|
||||
Assets Assets `json:"assets"`
|
||||
SupportedSignals SupportedSignals `json:"supported_signals"`
|
||||
DataCollected DataCollected `json:"dataCollected"`
|
||||
Strategy *CollectionStrategy `json:"telemetryCollectionStrategy"`
|
||||
}
|
||||
|
||||
// CollectionStrategy is cloud provider specific configuration for signal collection,
|
||||
// this is used by agent to understand the nitty-gritty for collecting telemetry for the cloud provider.
|
||||
type CollectionStrategy struct {
|
||||
AWS *AWSCollectionStrategy `json:"aws,omitempty"`
|
||||
}
|
||||
|
||||
// Assets represents the collection of dashboards.
|
||||
type Assets struct {
|
||||
Dashboards []Dashboard `json:"dashboards"`
|
||||
}
|
||||
|
||||
// SupportedSignals for cloud provider's service.
|
||||
type SupportedSignals struct {
|
||||
Logs bool `json:"logs"`
|
||||
Metrics bool `json:"metrics"`
|
||||
}
|
||||
|
||||
// DataCollected is curated static list of metrics and logs, this is shown as part of service overview.
|
||||
type DataCollected struct {
|
||||
Logs []CollectedLogAttribute `json:"logs"`
|
||||
Metrics []CollectedMetric `json:"metrics"`
|
||||
}
|
||||
|
||||
// CollectedLogAttribute represents a log attribute that is present in all log entries for a service,
|
||||
// this is shown as part of service overview.
|
||||
type CollectedLogAttribute struct {
|
||||
@@ -164,23 +169,56 @@ type AWSLogsStrategy struct {
|
||||
// This is used to show available pre-made dashboards for a service,
|
||||
// hence has additional fields like id, title and description
|
||||
type Dashboard struct {
|
||||
ID string `json:"id"`
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Definition dashboardtypes.StorableDashboardData `json:"definition,omitempty"`
|
||||
}
|
||||
|
||||
// SupportedServices is the map of supported services for each cloud provider.
|
||||
var SupportedServices = map[CloudProviderType][]ServiceID{
|
||||
CloudProviderTypeAWS: {
|
||||
{valuer.NewString("alb")},
|
||||
{valuer.NewString("api-gateway")},
|
||||
{valuer.NewString("dynamodb")},
|
||||
{valuer.NewString("ec2")},
|
||||
{valuer.NewString("ecs")},
|
||||
{valuer.NewString("eks")},
|
||||
{valuer.NewString("elasticache")},
|
||||
{valuer.NewString("lambda")},
|
||||
{valuer.NewString("msk")},
|
||||
{valuer.NewString("rds")},
|
||||
{valuer.NewString("s3sync")},
|
||||
{valuer.NewString("sns")},
|
||||
{valuer.NewString("sqs")},
|
||||
},
|
||||
}
|
||||
|
||||
// NewServiceID returns a new ServiceID from a string, validated against the supported services for the given cloud provider.
|
||||
func NewServiceID(provider CloudProviderType, service string) (ServiceID, error) {
|
||||
services, ok := SupportedServices[provider]
|
||||
if !ok {
|
||||
return ServiceID{}, errors.NewInvalidInputf(ErrCodeInvalidServiceID, "no services defined for cloud provider: %s", provider)
|
||||
}
|
||||
for _, s := range services {
|
||||
if s.StringValue() == service {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return ServiceID{}, errors.NewInvalidInputf(ErrCodeInvalidServiceID, "invalid service id %q for cloud provider %s", service, provider)
|
||||
}
|
||||
|
||||
// UTILS
|
||||
|
||||
// GetCloudIntegrationDashboardID returns the dashboard id for a cloud integration, given the cloud provider, service id, and dashboard id.
|
||||
// This is used to generate unique dashboard ids for cloud integration, and also to parse the dashboard id to get the cloud provider and service id when needed.
|
||||
func GetCloudIntegrationDashboardID(cloudProvider CloudProviderType, svcID, dashboardID string) string {
|
||||
return fmt.Sprintf("cloud-integration--%s--%s--%s", cloudProvider, svcID, dashboardID)
|
||||
func GetCloudIntegrationDashboardID(cloudProvider CloudProviderType, svcId, dashboardId string) string {
|
||||
return fmt.Sprintf("cloud-integration--%s--%s--%s", cloudProvider, svcId, dashboardId)
|
||||
}
|
||||
|
||||
// GetDashboardsFromAssets returns the list of dashboards for the cloud provider service from definition.
|
||||
func GetDashboardsFromAssets(
|
||||
svcID string,
|
||||
svcId string,
|
||||
orgID valuer.UUID,
|
||||
cloudProvider CloudProviderType,
|
||||
createdAt time.Time,
|
||||
@@ -191,7 +229,7 @@ func GetDashboardsFromAssets(
|
||||
for _, d := range assets.Dashboards {
|
||||
author := fmt.Sprintf("%s-integration", cloudProvider)
|
||||
dashboards = append(dashboards, &dashboardtypes.Dashboard{
|
||||
ID: GetCloudIntegrationDashboardID(cloudProvider, svcID, d.ID),
|
||||
ID: GetCloudIntegrationDashboardID(cloudProvider, svcId, d.Id),
|
||||
Locked: true,
|
||||
OrgID: orgID,
|
||||
Data: d.Definition,
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package cloudintegrationtypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type ServiceID struct{ valuer.String }
|
||||
|
||||
var (
|
||||
AWSServiceALB = ServiceID{valuer.NewString("alb")}
|
||||
AWSServiceAPIGateway = ServiceID{valuer.NewString("api-gateway")}
|
||||
AWSServiceDynamoDB = ServiceID{valuer.NewString("dynamodb")}
|
||||
AWSServiceEC2 = ServiceID{valuer.NewString("ec2")}
|
||||
AWSServiceECS = ServiceID{valuer.NewString("ecs")}
|
||||
AWSServiceEKS = ServiceID{valuer.NewString("eks")}
|
||||
AWSServiceElastiCache = ServiceID{valuer.NewString("elasticache")}
|
||||
AWSServiceLambda = ServiceID{valuer.NewString("lambda")}
|
||||
AWSServiceMSK = ServiceID{valuer.NewString("msk")}
|
||||
AWSServiceRDS = ServiceID{valuer.NewString("rds")}
|
||||
AWSServiceS3Sync = ServiceID{valuer.NewString("s3sync")}
|
||||
AWSServiceSNS = ServiceID{valuer.NewString("sns")}
|
||||
AWSServiceSQS = ServiceID{valuer.NewString("sqs")}
|
||||
)
|
||||
|
||||
func (ServiceID) Enum() []any {
|
||||
return []any{
|
||||
AWSServiceALB,
|
||||
AWSServiceAPIGateway,
|
||||
AWSServiceDynamoDB,
|
||||
AWSServiceEC2,
|
||||
AWSServiceECS,
|
||||
AWSServiceEKS,
|
||||
AWSServiceElastiCache,
|
||||
AWSServiceLambda,
|
||||
AWSServiceMSK,
|
||||
AWSServiceRDS,
|
||||
AWSServiceS3Sync,
|
||||
AWSServiceSNS,
|
||||
AWSServiceSQS,
|
||||
}
|
||||
}
|
||||
|
||||
// SupportedServices is the map of supported services for each cloud provider.
|
||||
var SupportedServices = map[CloudProviderType][]ServiceID{
|
||||
CloudProviderTypeAWS: {
|
||||
AWSServiceALB,
|
||||
AWSServiceAPIGateway,
|
||||
AWSServiceDynamoDB,
|
||||
AWSServiceEC2,
|
||||
AWSServiceECS,
|
||||
AWSServiceEKS,
|
||||
AWSServiceElastiCache,
|
||||
AWSServiceLambda,
|
||||
AWSServiceMSK,
|
||||
AWSServiceRDS,
|
||||
AWSServiceS3Sync,
|
||||
AWSServiceSNS,
|
||||
AWSServiceSQS,
|
||||
},
|
||||
}
|
||||
|
||||
// NewServiceID returns a new ServiceID from a string, validated against the supported services for the given cloud provider.
|
||||
func NewServiceID(provider CloudProviderType, service string) (ServiceID, error) {
|
||||
services, ok := SupportedServices[provider]
|
||||
if !ok {
|
||||
return ServiceID{}, errors.NewInvalidInputf(ErrCodeInvalidServiceID, "no services defined for cloud provider: %s", provider)
|
||||
}
|
||||
for _, s := range services {
|
||||
if s.StringValue() == service {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return ServiceID{}, errors.NewInvalidInputf(ErrCodeInvalidServiceID, "invalid service id %q for cloud provider %s", service, provider)
|
||||
}
|
||||
@@ -51,6 +51,15 @@ type DeprecatedUser struct {
|
||||
Role Role `json:"role"`
|
||||
}
|
||||
|
||||
type UpdatableSelfUser struct {
|
||||
DisplayName string `json:"displayName" required:"true"`
|
||||
}
|
||||
|
||||
type UpdatableUser struct {
|
||||
DisplayName string `json:"displayName" required:"true"`
|
||||
RoleNames []string `json:"roleNames" required:"true" nullable:"false"`
|
||||
}
|
||||
|
||||
type PostableRegisterOrgAndAdmin struct {
|
||||
Name string `json:"name"`
|
||||
Email valuer.Email `json:"email"`
|
||||
@@ -298,6 +307,9 @@ type UserStore interface {
|
||||
// Get user by reset password token
|
||||
GetUserByResetPasswordToken(ctx context.Context, token string) (*User, error)
|
||||
|
||||
// Get users having role by org id and role id
|
||||
GetUsersByOrgIDAndRoleID(ctx context.Context, orgID valuer.UUID, roleID valuer.UUID) ([]*User, error)
|
||||
|
||||
// Transaction
|
||||
RunInTx(ctx context.Context, cb func(ctx context.Context) error) error
|
||||
}
|
||||
|
||||
@@ -654,19 +654,6 @@ def get_oidc_domain(signoz: types.SigNoz, admin_token: str) -> dict:
|
||||
)
|
||||
|
||||
|
||||
def get_user_by_email(signoz: types.SigNoz, admin_token: str, email: str) -> dict:
|
||||
"""Helper to get a user by email."""
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
return next(
|
||||
(user for user in response.json()["data"] if user["email"] == email),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def perform_oidc_login(
|
||||
signoz: types.SigNoz, # pylint: disable=unused-argument
|
||||
idp: types.TestContainerIDP,
|
||||
|
||||
@@ -3,6 +3,9 @@ import os
|
||||
from typing import Any
|
||||
|
||||
import isodate
|
||||
import requests
|
||||
|
||||
from fixtures import types
|
||||
|
||||
|
||||
# parses the given timestamp string from ISO format to datetime.datetime
|
||||
@@ -31,3 +34,33 @@ def parse_duration(duration: Any) -> datetime.timedelta:
|
||||
def get_testdata_file_path(file: str) -> str:
|
||||
testdata_dir = os.path.join(os.path.dirname(__file__), "..", "testdata")
|
||||
return os.path.join(testdata_dir, file)
|
||||
|
||||
|
||||
def get_user_by_email(signoz: types.SigNoz, admin_token: str, email: str) -> dict:
|
||||
"""Helper to get a user by email."""
|
||||
headers = {"Authorization": f"Bearer {admin_token}"} if admin_token else {}
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers=headers,
|
||||
)
|
||||
return next(
|
||||
(user for user in response.json()["data"] if user["email"] == email),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def get_user_role_names(signoz: types.SigNoz, admin_token: str, user_id: str) -> list:
|
||||
"""Helper to get the user roles by user ID"""
|
||||
headers = {"Authorization": f"Bearer {admin_token}"} if admin_token else {}
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{user_id}/roles"),
|
||||
timeout=2,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
roles = response.json()["data"]
|
||||
if not roles:
|
||||
return []
|
||||
|
||||
return [role["name"] for role in roles]
|
||||
|
||||
@@ -12,11 +12,8 @@ from fixtures.auth import (
|
||||
USER_ADMIN_PASSWORD,
|
||||
add_license,
|
||||
)
|
||||
from fixtures.idputils import (
|
||||
get_saml_domain,
|
||||
get_user_by_email,
|
||||
perform_saml_login,
|
||||
)
|
||||
from fixtures.idputils import get_saml_domain, perform_saml_login
|
||||
from fixtures.utils import get_user_by_email, get_user_role_names
|
||||
from fixtures.types import Operation, SigNoz, TestContainerDocker, TestContainerIDP
|
||||
|
||||
|
||||
@@ -131,26 +128,12 @@ def test_saml_authn(
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
# Assert that the user was created in signoz.
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(
|
||||
user
|
||||
for user in user_response
|
||||
if user["email"] == "viewer@saml.integration.test"
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
found_user = get_user_by_email(signoz, admin_token, "viewer@saml.integration.test")
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
|
||||
# Confirm role
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_idp_initiated_saml_authn(
|
||||
@@ -182,26 +165,14 @@ def test_idp_initiated_saml_authn(
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
# Assert that the user was created in signoz.
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
found_user = get_user_by_email(
|
||||
signoz, admin_token, "viewer.idp.initiated@saml.integration.test"
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(
|
||||
user
|
||||
for user in user_response
|
||||
if user["email"] == "viewer.idp.initiated@saml.integration.test"
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
|
||||
# Confirm role
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_update_domain_with_group_mappings(
|
||||
@@ -271,7 +242,8 @@ def test_saml_role_mapping_single_group_admin(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "ADMIN"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-admin" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_role_mapping_single_group_editor(
|
||||
@@ -297,7 +269,8 @@ def test_saml_role_mapping_single_group_editor(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_role_mapping_multiple_groups_highest_wins(
|
||||
@@ -327,7 +300,8 @@ def test_saml_role_mapping_multiple_groups_highest_wins(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_role_mapping_explicit_viewer_group(
|
||||
@@ -354,7 +328,8 @@ def test_saml_role_mapping_explicit_viewer_group(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_role_mapping_unmapped_group_uses_default(
|
||||
@@ -380,7 +355,8 @@ def test_saml_role_mapping_unmapped_group_uses_default(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_update_domain_with_use_role_claim(
|
||||
@@ -457,7 +433,8 @@ def test_saml_role_mapping_role_claim_takes_precedence(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "ADMIN"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-admin" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_role_mapping_invalid_role_claim_fallback(
|
||||
@@ -487,7 +464,8 @@ def test_saml_role_mapping_invalid_role_claim_fallback(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_role_mapping_case_insensitive(
|
||||
@@ -517,7 +495,8 @@ def test_saml_role_mapping_case_insensitive(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "ADMIN"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-admin" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_name_mapping(
|
||||
@@ -545,7 +524,8 @@ def test_saml_name_mapping(
|
||||
assert (
|
||||
found_user["displayName"] == "Jane"
|
||||
) # We are only mapping the first name here
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_empty_name_fallback(
|
||||
@@ -570,7 +550,8 @@ def test_saml_empty_name_fallback(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_sso_login_activates_pending_invite_user(
|
||||
@@ -613,7 +594,8 @@ def test_saml_sso_login_activates_pending_invite_user(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
assert found_user is not None
|
||||
assert found_user["status"] == "active"
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_saml_sso_deleted_user_gets_new_user_on_login(
|
||||
@@ -680,7 +662,7 @@ def test_saml_sso_deleted_user_gets_new_user_on_login(
|
||||
|
||||
# Verify a NEW active user was auto-provisioned via SSO
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
@@ -694,4 +676,7 @@ def test_saml_sso_deleted_user_gets_new_user_on_login(
|
||||
)
|
||||
assert found_user is not None
|
||||
assert found_user["status"] == "active"
|
||||
assert found_user["role"] == "VIEWER" # default role from SSO domain config
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert (
|
||||
"signoz-viewer" in found_user_role_names
|
||||
) # default role from SSO domain config
|
||||
|
||||
@@ -11,11 +11,8 @@ from fixtures.auth import (
|
||||
USER_ADMIN_PASSWORD,
|
||||
add_license,
|
||||
)
|
||||
from fixtures.idputils import (
|
||||
get_oidc_domain,
|
||||
get_user_by_email,
|
||||
perform_oidc_login,
|
||||
)
|
||||
from fixtures.idputils import get_oidc_domain, perform_oidc_login
|
||||
from fixtures.utils import get_user_by_email, get_user_role_names
|
||||
from fixtures.types import Operation, SigNoz, TestContainerDocker, TestContainerIDP
|
||||
|
||||
|
||||
@@ -112,26 +109,12 @@ def test_oidc_authn(
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
# Assert that the user was created in signoz.
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(
|
||||
user
|
||||
for user in user_response
|
||||
if user["email"] == "viewer@oidc.integration.test"
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
found_user = get_user_by_email(signoz, admin_token, "viewer@oidc.integration.test")
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
|
||||
# Confirm role
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_update_domain_with_group_mappings(
|
||||
@@ -208,7 +191,8 @@ def test_oidc_role_mapping_single_group_admin(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "ADMIN"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-admin" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_role_mapping_single_group_editor(
|
||||
@@ -234,7 +218,8 @@ def test_oidc_role_mapping_single_group_editor(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_role_mapping_multiple_groups_highest_wins(
|
||||
@@ -264,7 +249,8 @@ def test_oidc_role_mapping_multiple_groups_highest_wins(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "ADMIN"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-admin" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_role_mapping_explicit_viewer_group(
|
||||
@@ -291,7 +277,8 @@ def test_oidc_role_mapping_explicit_viewer_group(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_role_mapping_unmapped_group_uses_default(
|
||||
@@ -317,7 +304,8 @@ def test_oidc_role_mapping_unmapped_group_uses_default(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_update_domain_with_use_role_claim(
|
||||
@@ -397,7 +385,8 @@ def test_oidc_role_mapping_role_claim_takes_precedence(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "ADMIN"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-admin" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_role_mapping_invalid_role_claim_fallback(
|
||||
@@ -429,7 +418,8 @@ def test_oidc_role_mapping_invalid_role_claim_fallback(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_role_mapping_case_insensitive(
|
||||
@@ -459,7 +449,8 @@ def test_oidc_role_mapping_case_insensitive(
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
|
||||
def test_oidc_name_mapping(
|
||||
@@ -482,20 +473,13 @@ def test_oidc_name_mapping(
|
||||
)
|
||||
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
users = response.json()["data"]
|
||||
found_user = next((u for u in users if u["email"] == email), None)
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
assert found_user is not None
|
||||
# Keycloak concatenates firstName + lastName into "name" claim
|
||||
assert found_user["displayName"] == "John Doe"
|
||||
assert found_user["role"] == "VIEWER" # Default role
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names # Default role
|
||||
|
||||
|
||||
def test_oidc_empty_name_uses_fallback(
|
||||
@@ -518,19 +502,12 @@ def test_oidc_empty_name_uses_fallback(
|
||||
)
|
||||
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
users = response.json()["data"]
|
||||
found_user = next((u for u in users if u["email"] == email), None)
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
|
||||
# User should still be created even with empty name
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
# Note: displayName may be empty - this is a known limitation
|
||||
|
||||
|
||||
@@ -570,16 +547,9 @@ def test_oidc_sso_login_activates_pending_invite_user(
|
||||
signoz, idp, driver, get_session_context, idp_login, email, "password123"
|
||||
)
|
||||
|
||||
# User should be active with ADMIN role from invite, not VIEWER from SSO
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
found_user = next(
|
||||
(user for user in response.json()["data"] if user["email"] == email),
|
||||
None,
|
||||
)
|
||||
# User should be active with VIEWER role from SSO
|
||||
found_user = get_user_by_email(signoz, admin_token, email)
|
||||
assert found_user is not None
|
||||
assert found_user["status"] == "active"
|
||||
assert found_user["role"] == "VIEWER"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-viewer" in found_user_role_names
|
||||
|
||||
@@ -5,6 +5,7 @@ import requests
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.logger import setup_logger
|
||||
from fixtures.utils import get_user_by_email, get_user_role_names
|
||||
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
@@ -74,31 +75,10 @@ def test_register(signoz: types.SigNoz, get_token: Callable[[str, str], str]) ->
|
||||
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(user for user in user_response if user["email"] == "admin@integration.test"),
|
||||
None,
|
||||
)
|
||||
|
||||
found_user = get_user_by_email(signoz, admin_token, "admin@integration.test")
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "ADMIN"
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v1/user/{found_user["id"]}"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.json()["data"]["role"] == "ADMIN"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-admin" in found_user_role_names
|
||||
|
||||
|
||||
def test_invite_and_register(
|
||||
@@ -120,21 +100,11 @@ def test_invite_and_register(
|
||||
assert invited_user["role"] == "EDITOR"
|
||||
|
||||
# Verify the user user appears in the users list but as pending_invite status
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(user for user in user_response if user["email"] == "editor@integration.test"),
|
||||
None,
|
||||
)
|
||||
found_user = get_user_by_email(signoz, admin_token, "editor@integration.test")
|
||||
assert found_user is not None
|
||||
assert found_user["status"] == "pending_invite"
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
reset_token = invited_user["token"]
|
||||
|
||||
@@ -152,7 +122,7 @@ def test_invite_and_register(
|
||||
|
||||
# Verify that an admin endpoint cannot be called by the editor user
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {editor_token}"},
|
||||
)
|
||||
@@ -160,24 +130,12 @@ def test_invite_and_register(
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
# Verify that the editor user status has been updated to ACTIVE
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={
|
||||
"Authorization": f"Bearer {get_token("admin@integration.test", "password123Z$")}"
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(user for user in user_response if user["email"] == "editor@integration.test"),
|
||||
None,
|
||||
)
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
found_user = get_user_by_email(signoz, admin_token, "editor@integration.test")
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user["role"] == "EDITOR"
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
assert found_user["displayName"] == "editor"
|
||||
assert found_user["email"] == "editor@integration.test"
|
||||
assert found_user["status"] == "active"
|
||||
@@ -221,25 +179,7 @@ def test_self_access(
|
||||
) -> None:
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(user for user in user_response if user["email"] == "editor@integration.test"),
|
||||
None,
|
||||
)
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v1/user/{found_user['id']}"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.json()["data"]["role"] == "EDITOR"
|
||||
found_user = get_user_by_email(signoz, admin_token, "editor@integration.test")
|
||||
assert found_user is not None
|
||||
found_user_role_names = get_user_role_names(signoz, admin_token, found_user["id"])
|
||||
assert "signoz-editor" in found_user_role_names
|
||||
|
||||
@@ -26,7 +26,7 @@ def test_api_key(signoz: types.SigNoz, get_token: Callable[[str, str], str]) ->
|
||||
assert "token" in pat_response["data"]
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"SIGNOZ-API-KEY": f"{pat_response["data"]["token"]}"},
|
||||
)
|
||||
@@ -85,7 +85,7 @@ def test_api_key_role(
|
||||
assert "token" in pat_response["data"]
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"SIGNOZ-API-KEY": f"{pat_response["data"]["token"]}"},
|
||||
)
|
||||
@@ -109,7 +109,7 @@ def test_api_key_role(
|
||||
assert "token" in pat_response["data"]
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"SIGNOZ-API-KEY": f"{pat_response["data"]["token"]}"},
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ from sqlalchemy import sql
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.logger import setup_logger
|
||||
from fixtures.utils import get_user_by_email
|
||||
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
@@ -35,23 +36,10 @@ def test_change_password(
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Get the user id
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(
|
||||
user
|
||||
for user in user_response
|
||||
if user["email"] == "admin+password@integration.test"
|
||||
),
|
||||
None,
|
||||
found_user = get_user_by_email(
|
||||
signoz, admin_token, "admin+password@integration.test"
|
||||
)
|
||||
assert found_user is not None
|
||||
|
||||
# Try logging in with the password
|
||||
token = get_token("admin+password@integration.test", "password123Z$")
|
||||
@@ -100,23 +88,10 @@ def test_reset_password(
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
|
||||
# Get the user id for admin+password@integration.test
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(
|
||||
user
|
||||
for user in user_response
|
||||
if user["email"] == "admin+password@integration.test"
|
||||
),
|
||||
None,
|
||||
found_user = get_user_by_email(
|
||||
signoz, admin_token, "admin+password@integration.test"
|
||||
)
|
||||
assert found_user is not None
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get(
|
||||
@@ -158,23 +133,10 @@ def test_reset_password_with_no_password(
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
|
||||
# Get the user id for admin+password@integration.test
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(
|
||||
user
|
||||
for user in user_response
|
||||
if user["email"] == "admin+password@integration.test"
|
||||
),
|
||||
None,
|
||||
found_user = get_user_by_email(
|
||||
signoz, admin_token, "admin+password@integration.test"
|
||||
)
|
||||
assert found_user is not None
|
||||
|
||||
with signoz.sqlstore.conn.connect() as conn:
|
||||
result = conn.execute(
|
||||
@@ -305,17 +267,7 @@ def test_forgot_password_creates_reset_token(
|
||||
|
||||
# Verify reset password token was created by querying the database
|
||||
# First, get the user ID
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(user for user in user_response if user["email"] == "forgot@integration.test"),
|
||||
None,
|
||||
)
|
||||
found_user = get_user_by_email(signoz, admin_token, "forgot@integration.test")
|
||||
assert found_user is not None
|
||||
|
||||
reset_token = None
|
||||
@@ -371,17 +323,7 @@ def test_reset_password_with_expired_token(
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
|
||||
# Get user ID for the forgot@integration.test user
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
user_response = response.json()["data"]
|
||||
found_user = next(
|
||||
(user for user in user_response if user["email"] == "forgot@integration.test"),
|
||||
None,
|
||||
)
|
||||
found_user = get_user_by_email(signoz, admin_token, "forgot@integration.test")
|
||||
assert found_user is not None
|
||||
|
||||
# Get org ID
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Callable, Tuple
|
||||
import requests
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.utils import get_user_role_names
|
||||
|
||||
|
||||
def test_change_role(
|
||||
@@ -40,7 +41,7 @@ def test_change_role(
|
||||
)
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user/me"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
@@ -60,20 +61,20 @@ def test_change_role(
|
||||
|
||||
# Change the new user's role - move to ADMIN
|
||||
response = requests.put(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v1/user/{new_user_id}"),
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{new_user_id}"),
|
||||
json={
|
||||
"displayName": "role change user",
|
||||
"role": "ADMIN",
|
||||
"roleNames": ["signoz-admin"],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Make some API calls again
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user/me"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
@@ -106,3 +107,302 @@ def test_change_role(
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
|
||||
def test_remove_all_roles(
|
||||
signoz: types.SigNoz,
|
||||
get_token: Callable[[str, str], str],
|
||||
get_tokens: Callable[[str, str], Tuple[str, str]],
|
||||
):
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
|
||||
# Create a new user as EDITOR
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/invite"),
|
||||
json={"email": "admin+noroles@integration.test", "role": "EDITOR"},
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.CREATED
|
||||
|
||||
invited_user = response.json()["data"]
|
||||
reset_token = invited_user["token"]
|
||||
|
||||
# Activate user via reset password
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/resetPassword"),
|
||||
json={"password": "password123Z$", "token": reset_token},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Login and get user id
|
||||
new_user_token, new_user_refresh_token = get_tokens(
|
||||
"admin+noroles@integration.test", "password123Z$"
|
||||
)
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
new_user_id = response.json()["data"]["id"]
|
||||
|
||||
# Validate the user has the editor role
|
||||
role_names = get_user_role_names(signoz, admin_token, new_user_id)
|
||||
assert role_names is not None
|
||||
assert "signoz-editor" in role_names
|
||||
|
||||
# Remove all roles
|
||||
response = requests.put(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{new_user_id}"),
|
||||
json={
|
||||
"displayName": "no roles user",
|
||||
"roleNames": [],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Validate the user has no roles
|
||||
role_names = get_user_role_names(signoz, admin_token, new_user_id)
|
||||
assert role_names is None or len(role_names) == 0
|
||||
|
||||
# Old token should be invalidated after role change
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.UNAUTHORIZED
|
||||
|
||||
# Token rotation should also fail for a user with no roles
|
||||
# (the session endpoint requires roles to build an identity)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/rotate"),
|
||||
json={
|
||||
"refreshToken": new_user_refresh_token,
|
||||
},
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert (
|
||||
response.status_code != HTTPStatus.OK
|
||||
), "token rotation should fail for user with no roles"
|
||||
|
||||
|
||||
def test_multiple_roles(
|
||||
signoz: types.SigNoz,
|
||||
get_token: Callable[[str, str], str],
|
||||
get_tokens: Callable[[str, str], Tuple[str, str]],
|
||||
):
|
||||
admin_token = get_token("admin@integration.test", "password123Z$")
|
||||
|
||||
# Create a new user as VIEWER
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/invite"),
|
||||
json={"email": "admin+multirole@integration.test", "role": "VIEWER"},
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.CREATED
|
||||
|
||||
invited_user = response.json()["data"]
|
||||
reset_token = invited_user["token"]
|
||||
|
||||
# Activate user via reset password
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/resetPassword"),
|
||||
json={"password": "password123Z$", "token": reset_token},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Login and get user id
|
||||
new_user_token, new_user_refresh_token = get_tokens(
|
||||
"admin+multirole@integration.test", "password123Z$"
|
||||
)
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
new_user_id = response.json()["data"]["id"]
|
||||
|
||||
# Validate user starts with viewer role
|
||||
role_names = get_user_role_names(signoz, admin_token, new_user_id)
|
||||
assert role_names is not None
|
||||
assert role_names == [
|
||||
"signoz-viewer"
|
||||
], f"expected ['signoz-viewer'], got {role_names}"
|
||||
|
||||
# As viewer, admin-only APIs should be forbidden
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/org/preferences"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
# Assign multiple roles: editor + viewer
|
||||
response = requests.put(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{new_user_id}"),
|
||||
json={
|
||||
"displayName": "multi role user",
|
||||
"roleNames": ["signoz-editor", "signoz-viewer"],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Validate user has both roles
|
||||
role_names = get_user_role_names(signoz, admin_token, new_user_id)
|
||||
assert role_names is not None
|
||||
assert sorted(role_names) == [
|
||||
"signoz-editor",
|
||||
"signoz-viewer",
|
||||
], f"expected ['signoz-editor', 'signoz-viewer'], got {sorted(role_names)}"
|
||||
|
||||
# Rotate token to pick up new roles
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/rotate"),
|
||||
json={"refreshToken": new_user_refresh_token},
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
rotate_response = response.json()["data"]
|
||||
new_user_token = rotate_response["accessToken"]
|
||||
new_user_refresh_token = rotate_response["refreshToken"]
|
||||
|
||||
# Verify /me includes both roles
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
me_role_names = sorted(r["name"] for r in response.json()["data"]["roles"])
|
||||
assert me_role_names == [
|
||||
"signoz-editor",
|
||||
"signoz-viewer",
|
||||
], f"expected ['signoz-editor', 'signoz-viewer'] in /me, got {me_role_names}"
|
||||
|
||||
# Editor+viewer still cannot access admin-only APIs
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/org/preferences"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
# Assign all three roles including admin
|
||||
response = requests.put(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{new_user_id}"),
|
||||
json={
|
||||
"displayName": "multi role user",
|
||||
"roleNames": ["signoz-admin", "signoz-editor", "signoz-viewer"],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
role_names = get_user_role_names(signoz, admin_token, new_user_id)
|
||||
assert sorted(role_names) == [
|
||||
"signoz-admin",
|
||||
"signoz-editor",
|
||||
"signoz-viewer",
|
||||
], f"expected all three roles, got {sorted(role_names)}"
|
||||
|
||||
# Rotate token to pick up admin role
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/rotate"),
|
||||
json={"refreshToken": new_user_refresh_token},
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
rotate_response = response.json()["data"]
|
||||
new_user_token = rotate_response["accessToken"]
|
||||
new_user_refresh_token = rotate_response["refreshToken"]
|
||||
|
||||
# Now with admin role, admin-only APIs should succeed
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/org/preferences"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
# Reduce back to single viewer role
|
||||
response = requests.put(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{new_user_id}"),
|
||||
json={
|
||||
"displayName": "multi role user",
|
||||
"roleNames": ["signoz-viewer"],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
role_names = get_user_role_names(signoz, admin_token, new_user_id)
|
||||
assert role_names == [
|
||||
"signoz-viewer"
|
||||
], f"expected ['signoz-viewer'] after reduction, got {role_names}"
|
||||
|
||||
# Rotate token to pick up reduced roles
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/rotate"),
|
||||
json={"refreshToken": new_user_refresh_token},
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
rotate_response = response.json()["data"]
|
||||
new_user_token = rotate_response["accessToken"]
|
||||
|
||||
# After reducing to viewer, admin-only APIs should be forbidden again
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/org/preferences"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
headers={"Authorization": f"Bearer {new_user_token}"},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.FORBIDDEN
|
||||
|
||||
@@ -52,7 +52,7 @@ def test_root_user_signoz_admin_assignment(
|
||||
|
||||
# Get the user from the /user/me endpoint and extract the id
|
||||
user_response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user/me"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
@@ -47,7 +47,7 @@ def test_user_invite_accept_role_grant(
|
||||
# Login with editor email and password
|
||||
editor_token = get_token(USER_EDITOR_EMAIL, USER_EDITOR_PASSWORD)
|
||||
user_me_response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user/me"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
headers={"Authorization": f"Bearer {editor_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
@@ -102,7 +102,7 @@ def test_user_update_role_grant(
|
||||
# Get the editor user's id
|
||||
editor_token = get_token(USER_EDITOR_EMAIL, USER_EDITOR_PASSWORD)
|
||||
user_me_response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user/me"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
headers={"Authorization": f"Bearer {editor_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
@@ -121,14 +121,14 @@ def test_user_update_role_grant(
|
||||
org_id = roles_data[0]["orgId"]
|
||||
|
||||
# Update the user's role to viewer
|
||||
update_payload = {"role": "VIEWER"}
|
||||
update_payload = {"roleNames": ["signoz-viewer"]}
|
||||
update_response = requests.put(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v1/user/{editor_id}"),
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{editor_id}"),
|
||||
json=update_payload,
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
assert update_response.status_code == HTTPStatus.OK
|
||||
assert update_response.status_code == HTTPStatus.NO_CONTENT
|
||||
|
||||
# Check that user no longer has the editor role in the db
|
||||
with signoz.sqlstore.conn.connect() as conn:
|
||||
@@ -179,7 +179,7 @@ def test_user_delete_role_revoke(
|
||||
# login with editor to get the user_id and check if user exists
|
||||
editor_token = get_token(USER_EDITOR_EMAIL, USER_EDITOR_PASSWORD)
|
||||
user_me_response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user/me"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users/me"),
|
||||
headers={"Authorization": f"Bearer {editor_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ def test_root_user_created(signoz: types.SigNoz) -> None:
|
||||
The root user service reconciles asynchronously after startup.
|
||||
|
||||
Phase 1: Poll /api/v1/version until setupCompleted=true.
|
||||
Phase 2: Poll /api/v1/user until it returns 200, confirming the root
|
||||
Phase 2: Poll /api/v2/users until it returns 200, confirming the root
|
||||
user actually exists and the impersonation provider works.
|
||||
"""
|
||||
# Phase 1: wait for setupCompleted
|
||||
@@ -39,13 +39,13 @@ def test_root_user_created(signoz: types.SigNoz) -> None:
|
||||
# Phase 2: wait for root user to be fully resolved
|
||||
for attempt in range(15):
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
)
|
||||
if response.status_code == HTTPStatus.OK:
|
||||
return
|
||||
logger.info(
|
||||
"Attempt %s: /api/v1/user returned %s, retrying ...",
|
||||
"Attempt %s: /api/v2/users returned %s, retrying ...",
|
||||
attempt + 1,
|
||||
response.status_code,
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import requests
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.logger import setup_logger
|
||||
from fixtures.utils import get_user_role_names
|
||||
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
@@ -32,7 +33,7 @@ def test_impersonated_user_is_admin(signoz: types.SigNoz) -> None:
|
||||
Listing users is an admin-only endpoint.
|
||||
"""
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/user"),
|
||||
signoz.self.host_configs["8080"].get("/api/v2/users"),
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
@@ -46,4 +47,5 @@ def test_impersonated_user_is_admin(signoz: types.SigNoz) -> None:
|
||||
None,
|
||||
)
|
||||
assert root_user is not None
|
||||
assert root_user["role"] == "ADMIN"
|
||||
root_user_role_names = get_user_role_names(signoz, None, root_user["id"])
|
||||
assert "signoz-admin" in root_user_role_names
|
||||
|
||||
Reference in New Issue
Block a user