2021-02-24 08:17:40 +01:00
|
|
|
/*
|
2024-09-24 09:42:07 +02:00
|
|
|
* Copyright (c) 2020 - present Cloudogu GmbH
|
2021-02-24 08:17:40 +01:00
|
|
|
*
|
2024-09-24 09:42:07 +02:00
|
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
|
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
|
|
|
* Software Foundation, version 3.
|
2021-02-24 08:17:40 +01:00
|
|
|
*
|
2024-09-24 09:42:07 +02:00
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
|
|
|
* details.
|
2021-02-24 08:17:40 +01:00
|
|
|
*
|
2024-09-24 09:42:07 +02:00
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
2021-02-24 08:17:40 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { ApiResult, useRequiredIndexLink } from "./base";
|
|
|
|
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
Permission Overview
Adds an overview of the permissions of a user including its groups. To do so, a new cache is introduced that stores the groups of a user, when the user is authenticated. In doing so, SCM-Manager can also list groups assigned by external authentication plugins such as LDAP. On the other hand, the user has to have been logged in at least once to get external groups, and even then the cached groups may be out of date when the overview is created. Internal groups will always be added correctly, nonetheless.
Due to the cache, another problem arised: On some logins, the xml dao for the cache failed to be read, because it was read and written at the same time. To fix this, a more thorough synchronization of the stores has been implemented.
Committed-by: Konstantin Schaper <konstantin.schaper@cloudogu.com>
Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
2023-02-09 10:29:05 +01:00
|
|
|
import { Link, Me, PermissionOverview, User, UserCollection, UserCreation } from "@scm-manager/ui-types";
|
2021-02-24 08:17:40 +01:00
|
|
|
import { apiClient } from "./apiclient";
|
|
|
|
|
import { createQueryString } from "./utils";
|
|
|
|
|
import { concat } from "./urls";
|
2021-06-28 13:19:03 +02:00
|
|
|
import { requiredLink } from "./links";
|
2021-02-24 08:17:40 +01:00
|
|
|
|
|
|
|
|
export type UseUsersRequest = {
|
|
|
|
|
page?: number | string;
|
|
|
|
|
search?: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useUsers = (request?: UseUsersRequest): ApiResult<UserCollection> => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const indexLink = useRequiredIndexLink("users");
|
|
|
|
|
|
|
|
|
|
const queryParams: Record<string, string> = {};
|
|
|
|
|
if (request?.search) {
|
|
|
|
|
queryParams.q = request.search;
|
|
|
|
|
}
|
|
|
|
|
if (request?.page) {
|
|
|
|
|
queryParams.page = request.page.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return useQuery<UserCollection, Error>(
|
|
|
|
|
["users", request?.search || "", request?.page || 0],
|
2021-06-28 13:19:03 +02:00
|
|
|
() => apiClient.get(`${indexLink}?${createQueryString(queryParams)}`).then((response) => response.json()),
|
2021-02-24 08:17:40 +01:00
|
|
|
{
|
|
|
|
|
onSuccess: (users: UserCollection) => {
|
2021-09-02 15:47:15 +02:00
|
|
|
users._embedded?.users.forEach((user: User) => queryClient.setQueryData(["user", user.name], user));
|
2021-06-28 13:19:03 +02:00
|
|
|
},
|
2021-02-24 08:17:40 +01:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useUser = (name: string): ApiResult<User> => {
|
|
|
|
|
const indexLink = useRequiredIndexLink("users");
|
|
|
|
|
return useQuery<User, Error>(["user", name], () =>
|
2021-06-28 13:19:03 +02:00
|
|
|
apiClient.get(concat(indexLink, name)).then((response) => response.json())
|
2021-02-24 08:17:40 +01:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
Permission Overview
Adds an overview of the permissions of a user including its groups. To do so, a new cache is introduced that stores the groups of a user, when the user is authenticated. In doing so, SCM-Manager can also list groups assigned by external authentication plugins such as LDAP. On the other hand, the user has to have been logged in at least once to get external groups, and even then the cached groups may be out of date when the overview is created. Internal groups will always be added correctly, nonetheless.
Due to the cache, another problem arised: On some logins, the xml dao for the cache failed to be read, because it was read and written at the same time. To fix this, a more thorough synchronization of the stores has been implemented.
Committed-by: Konstantin Schaper <konstantin.schaper@cloudogu.com>
Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
2023-02-09 10:29:05 +01:00
|
|
|
export const useUserPermissionOverview = (user: User): ApiResult<PermissionOverview> => {
|
|
|
|
|
const overviewLink = user._links.permissionOverview as Link;
|
|
|
|
|
return useQuery<PermissionOverview, Error>(["user", user.name, "permissionOverview"], () =>
|
|
|
|
|
apiClient.get(overviewLink.href).then((response) => response.json())
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-24 08:17:40 +01:00
|
|
|
const createUser = (link: string) => {
|
|
|
|
|
return (user: UserCreation) => {
|
|
|
|
|
return apiClient
|
|
|
|
|
.post(link, user, "application/vnd.scmm-user+json;v=2")
|
2021-06-28 13:19:03 +02:00
|
|
|
.then((response) => {
|
2021-02-24 08:17:40 +01:00
|
|
|
const location = response.headers.get("Location");
|
|
|
|
|
if (!location) {
|
|
|
|
|
throw new Error("Server does not return required Location header");
|
|
|
|
|
}
|
|
|
|
|
return apiClient.get(location);
|
|
|
|
|
})
|
2021-06-28 13:19:03 +02:00
|
|
|
.then((response) => response.json());
|
2021-02-24 08:17:40 +01:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useCreateUser = () => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const link = useRequiredIndexLink("users");
|
|
|
|
|
const { mutate, data, isLoading, error } = useMutation<User, Error, UserCreation>(createUser(link), {
|
2021-06-28 13:19:03 +02:00
|
|
|
onSuccess: (user) => {
|
2021-02-24 08:17:40 +01:00
|
|
|
queryClient.setQueryData(["user", user.name], user);
|
|
|
|
|
return queryClient.invalidateQueries(["users"]);
|
2021-06-28 13:19:03 +02:00
|
|
|
},
|
2021-02-24 08:17:40 +01:00
|
|
|
});
|
|
|
|
|
return {
|
|
|
|
|
create: (user: UserCreation) => mutate(user),
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
2021-06-28 13:19:03 +02:00
|
|
|
user: data,
|
2021-02-24 08:17:40 +01:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useUpdateUser = () => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { mutate, isLoading, error, data } = useMutation<unknown, Error, User>(
|
2021-06-28 13:19:03 +02:00
|
|
|
(user) => {
|
2021-02-24 08:17:40 +01:00
|
|
|
const updateUrl = (user._links.update as Link).href;
|
|
|
|
|
return apiClient.put(updateUrl, user, "application/vnd.scmm-user+json;v=2");
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
onSuccess: async (_, user) => {
|
|
|
|
|
await queryClient.invalidateQueries(["user", user.name]);
|
|
|
|
|
await queryClient.invalidateQueries(["users"]);
|
2021-06-28 13:19:03 +02:00
|
|
|
},
|
2021-02-24 08:17:40 +01:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
update: (user: User) => mutate(user),
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
2021-06-28 13:19:03 +02:00
|
|
|
isUpdated: !!data,
|
2021-02-24 08:17:40 +01:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useDeleteUser = () => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { mutate, isLoading, error, data } = useMutation<unknown, Error, User>(
|
2021-06-28 13:19:03 +02:00
|
|
|
(user) => {
|
2021-02-24 08:17:40 +01:00
|
|
|
const deleteUrl = (user._links.delete as Link).href;
|
|
|
|
|
return apiClient.delete(deleteUrl);
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
onSuccess: async (_, name) => {
|
2021-10-20 13:15:17 +02:00
|
|
|
await queryClient.removeQueries(["user", name]);
|
2021-02-24 08:17:40 +01:00
|
|
|
await queryClient.invalidateQueries(["users"]);
|
2021-06-28 13:19:03 +02:00
|
|
|
},
|
2021-02-24 08:17:40 +01:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
remove: (user: User) => mutate(user),
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
2021-06-28 13:19:03 +02:00
|
|
|
isDeleted: !!data,
|
2021-02-24 08:17:40 +01:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const convertToInternal = (url: string, newPassword: string) => {
|
|
|
|
|
return apiClient.put(
|
|
|
|
|
url,
|
|
|
|
|
{
|
2021-06-28 13:19:03 +02:00
|
|
|
newPassword,
|
2021-02-24 08:17:40 +01:00
|
|
|
},
|
|
|
|
|
"application/vnd.scmm-user+json;v=2"
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const convertToExternal = (url: string) => {
|
|
|
|
|
return apiClient.put(url, {}, "application/vnd.scmm-user+json;v=2");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type ConvertToInternalRequest = {
|
|
|
|
|
user: User;
|
|
|
|
|
password: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useConvertToInternal = () => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { mutate, isLoading, error, data } = useMutation<unknown, Error, ConvertToInternalRequest>(
|
|
|
|
|
({ user, password }) => convertToInternal((user._links.convertToInternal as Link).href, password),
|
|
|
|
|
{
|
|
|
|
|
onSuccess: async (_, { user }) => {
|
|
|
|
|
await queryClient.invalidateQueries(["user", user.name]);
|
|
|
|
|
await queryClient.invalidateQueries(["users"]);
|
2021-06-28 13:19:03 +02:00
|
|
|
},
|
2021-02-24 08:17:40 +01:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
convertToInternal: (user: User, password: string) => mutate({ user, password }),
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
2021-06-28 13:19:03 +02:00
|
|
|
isConverted: !!data,
|
2021-02-24 08:17:40 +01:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useConvertToExternal = () => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { mutate, isLoading, error, data } = useMutation<unknown, Error, User>(
|
2021-06-28 13:19:03 +02:00
|
|
|
(user) => convertToExternal((user._links.convertToExternal as Link).href),
|
2021-02-24 08:17:40 +01:00
|
|
|
{
|
|
|
|
|
onSuccess: async (_, user) => {
|
|
|
|
|
await queryClient.invalidateQueries(["user", user.name]);
|
|
|
|
|
await queryClient.invalidateQueries(["users"]);
|
2021-06-28 13:19:03 +02:00
|
|
|
},
|
2021-02-24 08:17:40 +01:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
convertToExternal: (user: User) => mutate(user),
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
2021-06-28 13:19:03 +02:00
|
|
|
isConverted: !!data,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const CONTENT_TYPE_PASSWORD_OVERWRITE = "application/vnd.scmm-passwordOverwrite+json;v=2";
|
|
|
|
|
|
|
|
|
|
export const useSetUserPassword = (user: User) => {
|
|
|
|
|
const { data, isLoading, error, mutate, reset } = useMutation<unknown, Error, string>((password) =>
|
|
|
|
|
apiClient.put(
|
|
|
|
|
requiredLink(user, "password"),
|
|
|
|
|
{
|
|
|
|
|
newPassword: password,
|
|
|
|
|
},
|
|
|
|
|
CONTENT_TYPE_PASSWORD_OVERWRITE
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
setPassword: (newPassword: string) => mutate(newPassword),
|
|
|
|
|
passwordOverwritten: !!data,
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
2021-09-02 15:47:15 +02:00
|
|
|
reset,
|
2021-06-28 13:19:03 +02:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const CONTENT_TYPE_PASSWORD_CHANGE = "application/vnd.scmm-passwordChange+json;v=2";
|
|
|
|
|
|
|
|
|
|
type ChangeUserPasswordRequest = {
|
|
|
|
|
oldPassword: string;
|
|
|
|
|
newPassword: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useChangeUserPassword = (user: User | Me) => {
|
|
|
|
|
const { data, isLoading, error, mutate, reset } = useMutation<unknown, Error, ChangeUserPasswordRequest>((request) =>
|
|
|
|
|
apiClient.put(requiredLink(user, "password"), request, CONTENT_TYPE_PASSWORD_CHANGE)
|
|
|
|
|
);
|
|
|
|
|
return {
|
|
|
|
|
changePassword: (oldPassword: string, newPassword: string) => mutate({ oldPassword, newPassword }),
|
|
|
|
|
passwordChanged: !!data,
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
2021-09-02 15:47:15 +02:00
|
|
|
reset,
|
2021-02-24 08:17:40 +01:00
|
|
|
};
|
|
|
|
|
};
|