mirror of https://github.com/halo-dev/halo
feat: add support for disabling/enabling user accounts (#7273)
#### What type of PR is this? /kind feature /area ui /milestone 2.20.x #### What this PR does / why we need it: Add support for disabling/enabling user accounts <img width="1207" alt="image" src="https://github.com/user-attachments/assets/a298e6f7-21a1-4b1c-86c3-1064a136e28c" /> #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/7250 #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note 支持在管理控制台禁用指定用户 ```pull/7284/head v2.20.16
parent
ca8bc52079
commit
4ad97cd58e
|
@ -7556,6 +7556,70 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/apis/console.api.security.halo.run/v1alpha1/users/{username}/disable": {
|
||||
"post": {
|
||||
"description": "Disable user by username",
|
||||
"operationId": "DisableUser",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Username",
|
||||
"in": "path",
|
||||
"name": "username",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "The user has been disabled."
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"UserV1alpha1Console"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/apis/console.api.security.halo.run/v1alpha1/users/{username}/enable": {
|
||||
"post": {
|
||||
"description": "Enable user by username",
|
||||
"operationId": "EnableUser",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Username",
|
||||
"in": "path",
|
||||
"name": "username",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "The user has been enabled."
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"UserV1alpha1Console"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/apis/content.halo.run/v1alpha1/categories": {
|
||||
"get": {
|
||||
"description": "List Category",
|
||||
|
|
|
@ -3374,6 +3374,70 @@
|
|||
"NotifierV1alpha1Console"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/apis/console.api.security.halo.run/v1alpha1/users/{username}/disable": {
|
||||
"post": {
|
||||
"description": "Disable user by username",
|
||||
"operationId": "DisableUser",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Username",
|
||||
"in": "path",
|
||||
"name": "username",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "The user has been disabled."
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"UserV1alpha1Console"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/apis/console.api.security.halo.run/v1alpha1/users/{username}/enable": {
|
||||
"post": {
|
||||
"description": "Enable user by username",
|
||||
"operationId": "EnableUser",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Username",
|
||||
"in": "path",
|
||||
"name": "username",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "The user has been enabled."
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"UserV1alpha1Console"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
|
|
@ -26,4 +26,9 @@ public interface UserService {
|
|||
Flux<User> listByEmail(String email);
|
||||
|
||||
String encryptPassword(String rawPassword);
|
||||
|
||||
Mono<User> disable(String username);
|
||||
|
||||
Mono<User> enable(String username);
|
||||
|
||||
}
|
||||
|
|
|
@ -11,4 +11,6 @@ public interface DeviceService {
|
|||
Mono<Void> changeSessionId(ServerWebExchange exchange);
|
||||
|
||||
Mono<Void> revoke(String principalName, String deviceId);
|
||||
|
||||
Mono<Void> revoke(String username);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
package run.halo.app.core.endpoint.console;
|
||||
|
||||
import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder;
|
||||
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import java.util.Objects;
|
||||
import org.springdoc.core.fn.builders.parameter.Builder;
|
||||
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.core.extension.endpoint.CustomEndpoint;
|
||||
import run.halo.app.core.user.service.UserService;
|
||||
import run.halo.app.extension.GroupVersion;
|
||||
|
||||
/**
|
||||
* User endpoint for console.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@Component
|
||||
class ConsoleUserEndpoint implements CustomEndpoint {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
ConsoleUserEndpoint(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> endpoint() {
|
||||
var tag = "UserV1alpha1Console";
|
||||
return RouterFunctions.nest(RequestPredicates.path("/users"), SpringdocRouteBuilder.route()
|
||||
.POST("/{username}/disable", this::handleDisableUser, ops -> {
|
||||
ops.operationId("DisableUser")
|
||||
.tag(tag)
|
||||
.description("Disable user by username")
|
||||
.parameter(Builder.parameterBuilder()
|
||||
.name("username")
|
||||
.in(ParameterIn.PATH)
|
||||
.description("Username")
|
||||
.required(true)
|
||||
.implementation(String.class)
|
||||
)
|
||||
.response(responseBuilder()
|
||||
.implementation(User.class)
|
||||
.description("The user has been disabled.")
|
||||
);
|
||||
})
|
||||
.POST("/{username}/enable", this::handleEnableUser, ops -> {
|
||||
ops.operationId("EnableUser")
|
||||
.tag(tag)
|
||||
.description("Enable user by username")
|
||||
.parameter(Builder.parameterBuilder()
|
||||
.name("username")
|
||||
.in(ParameterIn.PATH)
|
||||
.description("Username")
|
||||
.required(true)
|
||||
.implementation(String.class)
|
||||
)
|
||||
.response(responseBuilder()
|
||||
.implementation(User.class)
|
||||
.description("The user has been enabled.")
|
||||
);
|
||||
})
|
||||
.build());
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleEnableUser(ServerRequest request) {
|
||||
return userService.enable(request.pathVariable("username"))
|
||||
.switchIfEmpty(Mono.error(
|
||||
() -> new ServerWebInputException("The user was not found or has been enabled."))
|
||||
)
|
||||
.flatMap(user -> ServerResponse.ok().bodyValue(user));
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleDisableUser(ServerRequest request) {
|
||||
var username = request.pathVariable("username");
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map(SecurityContext::getAuthentication)
|
||||
.map(Authentication::getName)
|
||||
.switchIfEmpty(Mono.error(
|
||||
() -> new ServerWebInputException("The current user is not authenticated."))
|
||||
)
|
||||
.filter(currentUsername -> !Objects.equals(currentUsername, username))
|
||||
.switchIfEmpty(Mono.error(() -> new ServerWebInputException(
|
||||
"The user is the current user, can't disable it."
|
||||
)))
|
||||
.then(Mono.defer(() -> userService.disable(username)))
|
||||
.switchIfEmpty(Mono.error(
|
||||
() -> new ServerWebInputException("The user was not found or has been disabled."))
|
||||
)
|
||||
.flatMap(user -> ServerResponse.ok().bodyValue(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupVersion groupVersion() {
|
||||
return new GroupVersion("console.api.security.halo.run", "v1alpha1");
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@ import org.springframework.context.ApplicationEventPublisher;
|
|||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -45,6 +47,7 @@ import run.halo.app.infra.exception.EmailVerificationFailed;
|
|||
import run.halo.app.infra.exception.UnsatisfiedAttributeValueException;
|
||||
import run.halo.app.infra.exception.UserNotFoundException;
|
||||
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||
import run.halo.app.security.device.DeviceService;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
|
@ -66,6 +69,10 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
private final ExtensionGetter extensionGetter;
|
||||
|
||||
private final DeviceService deviceService;
|
||||
|
||||
private final ReactiveTransactionManager transactionManager;
|
||||
|
||||
private Clock clock = Clock.systemUTC();
|
||||
|
||||
void setClock(Clock clock) {
|
||||
|
@ -276,6 +283,25 @@ public class UserServiceImpl implements UserService {
|
|||
return passwordEncoder.encode(rawPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<User> disable(String username) {
|
||||
var tx = TransactionalOperator.create(transactionManager);
|
||||
return client.fetch(User.class, username)
|
||||
.filter(user -> !Boolean.TRUE.equals(user.getSpec().getDisabled()))
|
||||
.flatMap(user -> deviceService.revoke(username).thenReturn(user))
|
||||
.doOnNext(user -> user.getSpec().setDisabled(true))
|
||||
.flatMap(client::update)
|
||||
.as(tx::transactional);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<User> enable(String username) {
|
||||
return client.fetch(User.class, username)
|
||||
.filter(user -> Boolean.TRUE.equals(user.getSpec().getDisabled()))
|
||||
.doOnNext(user -> user.getSpec().setDisabled(false))
|
||||
.flatMap(client::update);
|
||||
}
|
||||
|
||||
void publishPasswordChangedEvent(String username) {
|
||||
eventPublisher.publishEvent(new PasswordChangedEvent(this, username));
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import static run.halo.app.extension.index.IndexAttributeFactory.simpleAttribute
|
|||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -177,6 +178,13 @@ public class SchemeInitializer implements ApplicationListener<ApplicationContext
|
|||
})
|
||||
)
|
||||
.orElseGet(Set::of))));
|
||||
indexSpecs.add(new IndexSpec()
|
||||
.setName("spec.disabled")
|
||||
.setIndexFunc(simpleAttribute(User.class, user ->
|
||||
Objects.requireNonNullElse(user.getSpec().getDisabled(), Boolean.FALSE)
|
||||
.toString())
|
||||
)
|
||||
);
|
||||
});
|
||||
schemeManager.register(ReverseProxy.class);
|
||||
schemeManager.register(Setting.class);
|
||||
|
|
|
@ -10,6 +10,7 @@ import lombok.Setter;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.CredentialsContainer;
|
||||
|
@ -65,7 +66,10 @@ public class UsernamePasswordHandler implements ServerAuthenticationSuccessHandl
|
|||
.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
|
||||
.switchIfEmpty(Mono.defer(
|
||||
() -> {
|
||||
URI location = URI.create("/login?error&method=local");
|
||||
var location = URI.create("/login?error&method=local");
|
||||
if (exception instanceof DisabledException) {
|
||||
location = URI.create("/login?error=account-disabled&method=local");
|
||||
}
|
||||
if (exception instanceof BadCredentialsException) {
|
||||
location = URI.create("/login?error=invalid-credential&method=local");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package run.halo.app.security.device;
|
||||
|
||||
import static run.halo.app.extension.ExtensionUtil.defaultSort;
|
||||
import static run.halo.app.infra.utils.IpAddressUtils.getClientIp;
|
||||
import static run.halo.app.security.authentication.rememberme.PersistentTokenBasedRememberMeServices.REMEMBER_ME_SERIES_REQUEST_NAME;
|
||||
|
||||
|
@ -24,8 +25,10 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.retry.Retry;
|
||||
import run.halo.app.core.extension.Device;
|
||||
import run.halo.app.extension.ListOptions;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.extension.index.query.QueryFactory;
|
||||
import run.halo.app.security.authentication.rememberme.PersistentRememberMeTokenRepository;
|
||||
|
||||
@Slf4j
|
||||
|
@ -132,6 +135,20 @@ public class DeviceServiceImpl implements DeviceService {
|
|||
.flatMap(revoked -> sessionRepository.deleteById(revoked.getSpec().getSessionId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> revoke(String username) {
|
||||
var listOptions = ListOptions.builder()
|
||||
.andQuery(QueryFactory.equal("spec.principalName", username))
|
||||
.build();
|
||||
return client.listAll(Device.class, listOptions, defaultSort())
|
||||
.flatMap(this::removeRememberMeToken)
|
||||
.flatMap(device -> sessionRepository.deleteById(device.getSpec().getSessionId())
|
||||
.thenReturn(device)
|
||||
)
|
||||
.flatMap(client::delete)
|
||||
.then();
|
||||
}
|
||||
|
||||
private Mono<Device> removeRememberMeToken(Device device) {
|
||||
var seriesId = device.getSpec().getRememberMeSeriesId();
|
||||
if (StringUtils.isBlank(seriesId)) {
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
<strong th:if="${error == 'rate-limit-exceeded'}">
|
||||
<span th:text="#{form.error.rateLimitExceeded}"></span>
|
||||
</strong>
|
||||
<strong th:if="${error == 'account-disabled'}">
|
||||
<span th:text="#{form.error.accountDisabled}"></span>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="alert" role="alert" th:if="${param.logout.size() > 0}">
|
||||
<strong th:text="#{form.messages.logoutSuccess}"></strong>
|
||||
|
|
|
@ -4,6 +4,7 @@ form.messages.oauth2Bind=当前登录未绑定账号,请尝试通过其他方
|
|||
form.messages.passwordReset=密码重置成功,请立即登录。
|
||||
form.error.invalidCredential=无效的凭证。
|
||||
form.error.rateLimitExceeded=请求过于频繁,请稍后再试。
|
||||
form.error.accountDisabled=账号已被禁用。
|
||||
form.rememberMe.label=保持登录会话
|
||||
form.submit=登录
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ form.messages.oauth2Bind=The current login is not bound to an account. Please tr
|
|||
form.messages.passwordReset=Password reset successfully, please login now.
|
||||
form.error.invalidCredential=Invalid credentials.
|
||||
form.error.rateLimitExceeded=Too many requests, please try again later.
|
||||
form.error.accountDisabled=Account has been disabled.
|
||||
form.rememberMe.label=Remember me
|
||||
form.submit=Login
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ form.messages.oauth2Bind=El inicio de sesión actual no está vinculado a una cu
|
|||
form.messages.passwordReset=¡Felicidades! La contraseña se ha restablecido con éxito.
|
||||
form.error.invalidCredential=Credenciales inválidas.
|
||||
form.error.rateLimitExceeded=Demasiadas solicitudes, por favor intente nuevamente más tarde.
|
||||
form.error.accountDisabled=La cuenta ha sido deshabilitada.
|
||||
form.rememberMe.label=Mantener sesión iniciada
|
||||
form.submit=Iniciar sesión
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ form.messages.oauth2Bind=當前登入未綁定至帳戶。請嘗試通過其他
|
|||
form.messages.passwordReset=密碼重置成功。請立即登入。
|
||||
form.error.invalidCredential=無效的憑證。
|
||||
form.error.rateLimitExceeded=請求過於頻繁,請稍後再試。
|
||||
form.error.accountDisabled=帳戶已被停用。
|
||||
form.form.rememberMe.label=保持登入會話
|
||||
form.form.submit=登入
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@ import {
|
|||
Toast,
|
||||
VButton,
|
||||
VDropdown,
|
||||
VDropdownDivider,
|
||||
VDropdownItem,
|
||||
VTabbar,
|
||||
VTag,
|
||||
} from "@halo-dev/components";
|
||||
import type { UserTab } from "@halo-dev/console-shared";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { useQuery, useQueryClient } from "@tanstack/vue-query";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import {
|
||||
computed,
|
||||
|
@ -30,8 +32,10 @@ import { useRoute, useRouter } from "vue-router";
|
|||
import GrantPermissionModal from "./components/GrantPermissionModal.vue";
|
||||
import UserEditingModal from "./components/UserEditingModal.vue";
|
||||
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
|
||||
import { useUserEnableDisable } from "./composables/use-user";
|
||||
import DetailTab from "./tabs/Detail.vue";
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const { currentUserHasPermission } = usePermission();
|
||||
const { t } = useI18n();
|
||||
const { currentUser } = useUserStore();
|
||||
|
@ -134,6 +138,8 @@ function onGrantPermissionModalClose() {
|
|||
grantPermissionModal.value = false;
|
||||
refetch();
|
||||
}
|
||||
|
||||
const { handleEnableOrDisableUser } = useUserEnableDisable();
|
||||
</script>
|
||||
<template>
|
||||
<UserEditingModal
|
||||
|
@ -162,9 +168,14 @@ function onGrantPermissionModalClose() {
|
|||
<UserAvatar :name="user?.user.metadata.name" />
|
||||
</div>
|
||||
<div class="block">
|
||||
<h1 class="truncate text-lg font-bold text-gray-900">
|
||||
{{ user?.user.spec.displayName }}
|
||||
</h1>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1 class="truncate text-lg font-bold text-gray-900">
|
||||
{{ user?.user.spec.displayName }}
|
||||
</h1>
|
||||
<VTag v-if="user?.user.spec.disabled">
|
||||
{{ $t("core.user.fields.disabled") }}
|
||||
</VTag>
|
||||
</div>
|
||||
<span v-if="!isLoading" class="text-sm text-gray-600">
|
||||
@{{ user?.user.metadata.name }}
|
||||
</span>
|
||||
|
@ -195,6 +206,33 @@ function onGrantPermissionModalClose() {
|
|||
>
|
||||
{{ $t("core.user.detail.actions.grant_permission.title") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownDivider
|
||||
v-if="currentUser?.metadata.name !== user?.user.metadata.name"
|
||||
/>
|
||||
<VDropdownItem
|
||||
v-if="
|
||||
!!user &&
|
||||
currentUser?.metadata.name !== user?.user.metadata.name
|
||||
"
|
||||
type="danger"
|
||||
@click="
|
||||
handleEnableOrDisableUser({
|
||||
name: user.user.metadata.name,
|
||||
operation: user.user.spec.disabled ? 'enable' : 'disable',
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['user-detail'],
|
||||
});
|
||||
},
|
||||
})
|
||||
"
|
||||
>
|
||||
{{
|
||||
user.user.spec.disabled
|
||||
? $t("core.user.operations.enable.title")
|
||||
: $t("core.user.operations.disable.title")
|
||||
}}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem
|
||||
v-if="
|
||||
user &&
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import { useFetchRole } from "@/composables/use-role";
|
||||
import { rbacAnnotations } from "@/constants/annotations";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
import { usePermission } from "@/utils/permission";
|
||||
import type { ListedUser, User } from "@halo-dev/api-client";
|
||||
import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
|
||||
|
@ -14,40 +13,28 @@ import {
|
|||
IconShieldUser,
|
||||
IconUserSettings,
|
||||
Toast,
|
||||
VAvatar,
|
||||
VButton,
|
||||
VCard,
|
||||
VDropdownItem,
|
||||
VEmpty,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VLoading,
|
||||
VPageHeader,
|
||||
VPagination,
|
||||
VSpace,
|
||||
VStatusDot,
|
||||
VTag,
|
||||
} from "@halo-dev/components";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import GrantPermissionModal from "./components/GrantPermissionModal.vue";
|
||||
import UserCreationModal from "./components/UserCreationModal.vue";
|
||||
import UserEditingModal from "./components/UserEditingModal.vue";
|
||||
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
|
||||
import UserListItem from "./components/UserListItem.vue";
|
||||
|
||||
const { currentUserHasPermission } = usePermission();
|
||||
const { t } = useI18n();
|
||||
|
||||
const checkedAll = ref(false);
|
||||
const editingModal = ref<boolean>(false);
|
||||
const creationModal = ref<boolean>(false);
|
||||
const passwordChangeModal = ref<boolean>(false);
|
||||
const grantPermissionModal = ref<boolean>(false);
|
||||
|
||||
const selectedUserNames = ref<string[]>([]);
|
||||
const selectedUser = ref<User>();
|
||||
const keyword = useRouteQuery<string>("keyword", "");
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
@ -122,34 +109,8 @@ const {
|
|||
|
||||
return hasDeletingData ? 1000 : false;
|
||||
},
|
||||
onSuccess() {
|
||||
selectedUser.value = undefined;
|
||||
},
|
||||
});
|
||||
|
||||
const handleDelete = async (user: User) => {
|
||||
Dialog.warning({
|
||||
title: t("core.user.operations.delete.title"),
|
||||
description: t("core.common.dialog.descriptions.cannot_be_recovered"),
|
||||
confirmType: "danger",
|
||||
confirmText: t("core.common.buttons.confirm"),
|
||||
cancelText: t("core.common.buttons.cancel"),
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await coreApiClient.user.deleteUser({
|
||||
name: user.metadata.name,
|
||||
});
|
||||
|
||||
Toast.success(t("core.common.toast.delete_success"));
|
||||
} catch (e) {
|
||||
console.error("Failed to delete user", e);
|
||||
} finally {
|
||||
await refetch();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteInBatch = async () => {
|
||||
Dialog.warning({
|
||||
title: t("core.user.operations.delete_in_batch.title"),
|
||||
|
@ -184,10 +145,7 @@ watch(selectedUserNames, (newValue) => {
|
|||
});
|
||||
|
||||
const checkSelection = (user: User) => {
|
||||
return (
|
||||
user.metadata.name === selectedUser.value?.metadata.name ||
|
||||
selectedUserNames.value.includes(user.metadata.name)
|
||||
);
|
||||
return selectedUserNames.value.includes(user.metadata.name);
|
||||
};
|
||||
|
||||
const handleCheckAllChange = (e: Event) => {
|
||||
|
@ -209,21 +167,6 @@ const handleCheckAllChange = (e: Event) => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleOpenCreateModal = (user: User) => {
|
||||
selectedUser.value = user;
|
||||
editingModal.value = true;
|
||||
};
|
||||
|
||||
const handleOpenPasswordChangeModal = (user: User) => {
|
||||
selectedUser.value = user;
|
||||
passwordChangeModal.value = true;
|
||||
};
|
||||
|
||||
const handleOpenGrantPermissionModal = (user: User) => {
|
||||
selectedUser.value = user;
|
||||
grantPermissionModal.value = true;
|
||||
};
|
||||
|
||||
// Route query action
|
||||
const routeQueryAction = useRouteQuery<string | undefined>("action");
|
||||
|
||||
|
@ -237,44 +180,10 @@ function onCreationModalClose() {
|
|||
creationModal.value = false;
|
||||
routeQueryAction.value = undefined;
|
||||
}
|
||||
|
||||
function onEditingModalClose() {
|
||||
editingModal.value = false;
|
||||
selectedUser.value = undefined;
|
||||
}
|
||||
|
||||
function onPasswordChangeModalClose() {
|
||||
passwordChangeModal.value = false;
|
||||
refetch();
|
||||
}
|
||||
|
||||
function onGrantPermissionModalClose() {
|
||||
grantPermissionModal.value = false;
|
||||
selectedUser.value = undefined;
|
||||
refetch();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<UserEditingModal
|
||||
v-if="editingModal && selectedUser"
|
||||
:user="selectedUser"
|
||||
@close="onEditingModalClose"
|
||||
/>
|
||||
|
||||
<UserCreationModal v-if="creationModal" @close="onCreationModalClose" />
|
||||
|
||||
<UserPasswordChangeModal
|
||||
v-if="passwordChangeModal"
|
||||
:user="selectedUser"
|
||||
@close="onPasswordChangeModalClose"
|
||||
/>
|
||||
|
||||
<GrantPermissionModal
|
||||
v-if="grantPermissionModal"
|
||||
:user="selectedUser"
|
||||
@close="onGrantPermissionModalClose"
|
||||
/>
|
||||
|
||||
<VPageHeader :title="$t('core.user.title')">
|
||||
<template #icon>
|
||||
<IconUserSettings class="mr-2 self-center" />
|
||||
|
@ -413,7 +322,7 @@ function onGrantPermissionModalClose() {
|
|||
<VButton
|
||||
v-permission="['system:users:manage']"
|
||||
type="secondary"
|
||||
@click="editingModal = true"
|
||||
@click="creationModal = true"
|
||||
>
|
||||
<template #icon>
|
||||
<IconAddCircle class="h-full w-full" />
|
||||
|
@ -431,7 +340,7 @@ function onGrantPermissionModalClose() {
|
|||
role="list"
|
||||
>
|
||||
<li v-for="(user, index) in users" :key="index">
|
||||
<VEntity :is-selected="checkSelection(user.user)">
|
||||
<UserListItem :user="user" :is-selected="checkSelection(user.user)">
|
||||
<template
|
||||
v-if="currentUserHasPermission(['system:users:manage'])"
|
||||
#checkbox
|
||||
|
@ -439,7 +348,7 @@ function onGrantPermissionModalClose() {
|
|||
<input
|
||||
v-model="selectedUserNames"
|
||||
:value="user.user.metadata.name"
|
||||
name="post-checkbox"
|
||||
name="user-checkbox"
|
||||
type="checkbox"
|
||||
:disabled="
|
||||
user.user.metadata.name ===
|
||||
|
@ -447,95 +356,7 @@ function onGrantPermissionModalClose() {
|
|||
"
|
||||
/>
|
||||
</template>
|
||||
<template #start>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<VAvatar
|
||||
:alt="user.user.spec.displayName"
|
||||
:src="user.user.spec.avatar"
|
||||
size="md"
|
||||
></VAvatar>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:title="user.user.spec.displayName"
|
||||
:description="user.user.metadata.name"
|
||||
:route="{
|
||||
name: 'UserDetail',
|
||||
params: { name: user.user.metadata.name },
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #end>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<VSpace>
|
||||
<VTag
|
||||
v-for="role in user.roles"
|
||||
:key="role.metadata.name"
|
||||
>
|
||||
<template #leftIcon>
|
||||
<IconShieldUser />
|
||||
</template>
|
||||
{{
|
||||
role.metadata.annotations?.[
|
||||
rbacAnnotations.DISPLAY_NAME
|
||||
] || role.metadata.name
|
||||
}}
|
||||
</VTag>
|
||||
</VSpace>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField v-if="user.user.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot
|
||||
v-tooltip="$t('core.common.status.deleting')"
|
||||
state="warning"
|
||||
animate
|
||||
/>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<span class="truncate text-xs tabular-nums text-gray-500">
|
||||
{{ formatDatetime(user.user.metadata.creationTimestamp) }}
|
||||
</span>
|
||||
</template>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template
|
||||
v-if="currentUserHasPermission(['system:users:manage'])"
|
||||
#dropdownItems
|
||||
>
|
||||
<VDropdownItem @click="handleOpenCreateModal(user.user)">
|
||||
{{ $t("core.user.operations.update_profile.title") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem
|
||||
@click="handleOpenPasswordChangeModal(user.user)"
|
||||
>
|
||||
{{ $t("core.user.operations.change_password.title") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem
|
||||
v-if="
|
||||
userStore.currentUser?.metadata.name !==
|
||||
user.user.metadata.name
|
||||
"
|
||||
@click="handleOpenGrantPermissionModal(user.user)"
|
||||
>
|
||||
{{ $t("core.user.operations.grant_permission.title") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem
|
||||
v-if="
|
||||
userStore.currentUser?.metadata.name !==
|
||||
user.user.metadata.name
|
||||
"
|
||||
type="danger"
|
||||
@click="handleDelete(user.user)"
|
||||
>
|
||||
{{ $t("core.common.buttons.delete") }}
|
||||
</VDropdownItem>
|
||||
</template>
|
||||
</VEntity>
|
||||
</UserListItem>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
<script setup lang="ts">
|
||||
import { rbacAnnotations } from "@/constants/annotations";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
import { usePermission } from "@/utils/permission";
|
||||
import { coreApiClient, type ListedUser } from "@halo-dev/api-client";
|
||||
import {
|
||||
Dialog,
|
||||
IconShieldUser,
|
||||
Toast,
|
||||
VAvatar,
|
||||
VDropdownDivider,
|
||||
VDropdownItem,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VSpace,
|
||||
VStatusDot,
|
||||
VTag,
|
||||
} from "@halo-dev/components";
|
||||
import { useQueryClient } from "@tanstack/vue-query";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useUserEnableDisable } from "../composables/use-user";
|
||||
import GrantPermissionModal from "./GrantPermissionModal.vue";
|
||||
import UserEditingModal from "./UserEditingModal.vue";
|
||||
import UserPasswordChangeModal from "./UserPasswordChangeModal.vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
user: ListedUser;
|
||||
isSelected?: boolean;
|
||||
}>(),
|
||||
{ isSelected: false }
|
||||
);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const { currentUserHasPermission } = usePermission();
|
||||
const { t } = useI18n();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
|
||||
const handleDelete = async () => {
|
||||
Dialog.warning({
|
||||
title: t("core.user.operations.delete.title"),
|
||||
description: t("core.common.dialog.descriptions.cannot_be_recovered"),
|
||||
confirmType: "danger",
|
||||
confirmText: t("core.common.buttons.confirm"),
|
||||
cancelText: t("core.common.buttons.cancel"),
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await coreApiClient.user.deleteUser({
|
||||
name: props.user.user.metadata.name,
|
||||
});
|
||||
|
||||
Toast.success(t("core.common.toast.delete_success"));
|
||||
} catch (e) {
|
||||
console.error("Failed to delete user", e);
|
||||
} finally {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["users"],
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const grantPermissionModal = ref(false);
|
||||
|
||||
function onGrantPermissionModalClose() {
|
||||
grantPermissionModal.value = false;
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["users"],
|
||||
});
|
||||
}
|
||||
|
||||
const passwordChangeModal = ref<boolean>(false);
|
||||
|
||||
function onPasswordChangeModalClose() {
|
||||
passwordChangeModal.value = false;
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["users"],
|
||||
});
|
||||
}
|
||||
|
||||
const editingModal = ref<boolean>(false);
|
||||
|
||||
function onEditingModalClose() {
|
||||
editingModal.value = false;
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["users"],
|
||||
});
|
||||
}
|
||||
|
||||
const { handleEnableOrDisableUser } = useUserEnableDisable();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UserEditingModal
|
||||
v-if="editingModal"
|
||||
:user="user.user"
|
||||
@close="onEditingModalClose"
|
||||
/>
|
||||
<GrantPermissionModal
|
||||
v-if="grantPermissionModal"
|
||||
:user="user.user"
|
||||
@close="onGrantPermissionModalClose"
|
||||
/>
|
||||
<UserPasswordChangeModal
|
||||
v-if="passwordChangeModal"
|
||||
:user="user.user"
|
||||
@close="onPasswordChangeModalClose"
|
||||
/>
|
||||
<VEntity :is-selected="isSelected">
|
||||
<template #checkbox>
|
||||
<slot name="checkbox" />
|
||||
</template>
|
||||
<template #start>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<VAvatar
|
||||
:alt="user.user.spec.displayName"
|
||||
:src="user.user.spec.avatar"
|
||||
size="md"
|
||||
></VAvatar>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
:title="user.user.spec.displayName"
|
||||
:description="user.user.metadata.name"
|
||||
:route="{
|
||||
name: 'UserDetail',
|
||||
params: { name: user.user.metadata.name },
|
||||
}"
|
||||
>
|
||||
<template v-if="user.user.spec.disabled" #extra>
|
||||
<VTag>
|
||||
{{ $t("core.user.fields.disabled") }}
|
||||
</VTag>
|
||||
</template>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template #end>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<VSpace>
|
||||
<VTag v-for="role in user.roles" :key="role.metadata.name">
|
||||
<template #leftIcon>
|
||||
<IconShieldUser />
|
||||
</template>
|
||||
{{
|
||||
role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
|
||||
role.metadata.name
|
||||
}}
|
||||
</VTag>
|
||||
</VSpace>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField v-if="user.user.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot
|
||||
v-tooltip="$t('core.common.status.deleting')"
|
||||
state="warning"
|
||||
animate
|
||||
/>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<span class="truncate text-xs tabular-nums text-gray-500">
|
||||
{{ formatDatetime(user.user.metadata.creationTimestamp) }}
|
||||
</span>
|
||||
</template>
|
||||
</VEntityField>
|
||||
</template>
|
||||
<template
|
||||
v-if="currentUserHasPermission(['system:users:manage'])"
|
||||
#dropdownItems
|
||||
>
|
||||
<VDropdownItem @click="editingModal = true">
|
||||
{{ $t("core.user.operations.update_profile.title") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem @click="passwordChangeModal = true">
|
||||
{{ $t("core.user.operations.change_password.title") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem
|
||||
v-if="currentUser?.metadata.name !== user.user.metadata.name"
|
||||
@click="grantPermissionModal = true"
|
||||
>
|
||||
{{ $t("core.user.operations.grant_permission.title") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownDivider
|
||||
v-if="currentUser?.metadata.name !== user.user.metadata.name"
|
||||
/>
|
||||
<VDropdownItem
|
||||
v-if="currentUser?.metadata.name !== user.user.metadata.name"
|
||||
type="danger"
|
||||
@click="
|
||||
handleEnableOrDisableUser({
|
||||
name: user.user.metadata.name,
|
||||
operation: user.user.spec.disabled ? 'enable' : 'disable',
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['users'],
|
||||
});
|
||||
},
|
||||
})
|
||||
"
|
||||
>
|
||||
{{
|
||||
user.user.spec.disabled
|
||||
? $t("core.user.operations.enable.title")
|
||||
: $t("core.user.operations.disable.title")
|
||||
}}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem
|
||||
v-if="currentUser?.metadata.name !== user.user.metadata.name"
|
||||
type="danger"
|
||||
@click="handleDelete"
|
||||
>
|
||||
{{ $t("core.common.buttons.delete") }}
|
||||
</VDropdownItem>
|
||||
</template>
|
||||
</VEntity>
|
||||
</template>
|
|
@ -1,7 +1,9 @@
|
|||
import type { User } from "@halo-dev/api-client";
|
||||
import { coreApiClient } from "@halo-dev/api-client";
|
||||
import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
|
||||
import { Dialog, Toast } from "@halo-dev/components";
|
||||
import type { Ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
interface useUserFetchReturn {
|
||||
users: Ref<User[]>;
|
||||
|
@ -43,3 +45,62 @@ export function useUserFetch(options?: {
|
|||
handleFetchUsers,
|
||||
};
|
||||
}
|
||||
|
||||
export function useUserEnableDisable() {
|
||||
const { t } = useI18n();
|
||||
|
||||
const handleEnableOrDisableUser = ({
|
||||
name,
|
||||
operation,
|
||||
onSuccess,
|
||||
}: {
|
||||
name: string;
|
||||
operation: "enable" | "disable";
|
||||
onSuccess?: () => void;
|
||||
}) => {
|
||||
const operations = {
|
||||
enable: {
|
||||
title: t("core.user.operations.enable.title"),
|
||||
description: t("core.user.operations.enable.description"),
|
||||
value: false,
|
||||
},
|
||||
disable: {
|
||||
title: t("core.user.operations.disable.title"),
|
||||
description: t("core.user.operations.disable.description"),
|
||||
value: true,
|
||||
},
|
||||
};
|
||||
|
||||
const operationConfig = operations[operation];
|
||||
|
||||
Dialog.warning({
|
||||
title: operationConfig.title,
|
||||
description: operationConfig.description,
|
||||
confirmType: "danger",
|
||||
confirmText: t("core.common.buttons.confirm"),
|
||||
cancelText: t("core.common.buttons.cancel"),
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
if (operation == "enable") {
|
||||
await consoleApiClient.user.enableUser({
|
||||
username: name,
|
||||
});
|
||||
} else {
|
||||
await consoleApiClient.user.disableUser({
|
||||
username: name,
|
||||
});
|
||||
}
|
||||
|
||||
Toast.success(t("core.common.toast.operation_success"));
|
||||
onSuccess?.();
|
||||
} catch (e) {
|
||||
console.error("Failed to enable or disable user", e);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
handleEnableOrDisableUser,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -212,6 +212,88 @@ export const UserV1alpha1ConsoleApiAxiosParamCreator = function (configuration?:
|
|||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Disable user by username
|
||||
* @param {string} username Username
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
disableUser: async (username: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'username' is not null or undefined
|
||||
assertParamExists('disableUser', 'username', username)
|
||||
const localVarPath = `/apis/console.api.security.halo.run/v1alpha1/users/{username}/disable`
|
||||
.replace(`{${"username"}}`, encodeURIComponent(String(username)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication basicAuth required
|
||||
// http basic authentication required
|
||||
setBasicAuthToObject(localVarRequestOptions, configuration)
|
||||
|
||||
// authentication bearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Enable user by username
|
||||
* @param {string} username Username
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
enableUser: async (username: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'username' is not null or undefined
|
||||
assertParamExists('enableUser', 'username', username)
|
||||
const localVarPath = `/apis/console.api.security.halo.run/v1alpha1/users/{username}/enable`
|
||||
.replace(`{${"username"}}`, encodeURIComponent(String(username)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication basicAuth required
|
||||
// http basic authentication required
|
||||
setBasicAuthToObject(localVarRequestOptions, configuration)
|
||||
|
||||
// authentication bearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
@ -699,6 +781,30 @@ export const UserV1alpha1ConsoleApiFp = function(configuration?: Configuration)
|
|||
const localVarOperationServerBasePath = operationServerMap['UserV1alpha1ConsoleApi.deleteUserAvatar']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Disable user by username
|
||||
* @param {string} username Username
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async disableUser(username: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<User>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.disableUser(username, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['UserV1alpha1ConsoleApi.disableUser']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Enable user by username
|
||||
* @param {string} username Username
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async enableUser(username: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<User>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.enableUser(username, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['UserV1alpha1ConsoleApi.enableUser']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Get current user detail
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -860,6 +966,24 @@ export const UserV1alpha1ConsoleApiFactory = function (configuration?: Configura
|
|||
deleteUserAvatar(requestParameters: UserV1alpha1ConsoleApiDeleteUserAvatarRequest, options?: RawAxiosRequestConfig): AxiosPromise<User> {
|
||||
return localVarFp.deleteUserAvatar(requestParameters.name, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Disable user by username
|
||||
* @param {UserV1alpha1ConsoleApiDisableUserRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
disableUser(requestParameters: UserV1alpha1ConsoleApiDisableUserRequest, options?: RawAxiosRequestConfig): AxiosPromise<User> {
|
||||
return localVarFp.disableUser(requestParameters.username, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Enable user by username
|
||||
* @param {UserV1alpha1ConsoleApiEnableUserRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
enableUser(requestParameters: UserV1alpha1ConsoleApiEnableUserRequest, options?: RawAxiosRequestConfig): AxiosPromise<User> {
|
||||
return localVarFp.enableUser(requestParameters.username, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Get current user detail
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -1006,6 +1130,34 @@ export interface UserV1alpha1ConsoleApiDeleteUserAvatarRequest {
|
|||
readonly name: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for disableUser operation in UserV1alpha1ConsoleApi.
|
||||
* @export
|
||||
* @interface UserV1alpha1ConsoleApiDisableUserRequest
|
||||
*/
|
||||
export interface UserV1alpha1ConsoleApiDisableUserRequest {
|
||||
/**
|
||||
* Username
|
||||
* @type {string}
|
||||
* @memberof UserV1alpha1ConsoleApiDisableUser
|
||||
*/
|
||||
readonly username: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for enableUser operation in UserV1alpha1ConsoleApi.
|
||||
* @export
|
||||
* @interface UserV1alpha1ConsoleApiEnableUserRequest
|
||||
*/
|
||||
export interface UserV1alpha1ConsoleApiEnableUserRequest {
|
||||
/**
|
||||
* Username
|
||||
* @type {string}
|
||||
* @memberof UserV1alpha1ConsoleApiEnableUser
|
||||
*/
|
||||
readonly username: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for getPermissions operation in UserV1alpha1ConsoleApi.
|
||||
* @export
|
||||
|
@ -1225,6 +1377,28 @@ export class UserV1alpha1ConsoleApi extends BaseAPI {
|
|||
return UserV1alpha1ConsoleApiFp(this.configuration).deleteUserAvatar(requestParameters.name, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable user by username
|
||||
* @param {UserV1alpha1ConsoleApiDisableUserRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof UserV1alpha1ConsoleApi
|
||||
*/
|
||||
public disableUser(requestParameters: UserV1alpha1ConsoleApiDisableUserRequest, options?: RawAxiosRequestConfig) {
|
||||
return UserV1alpha1ConsoleApiFp(this.configuration).disableUser(requestParameters.username, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable user by username
|
||||
* @param {UserV1alpha1ConsoleApiEnableUserRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof UserV1alpha1ConsoleApi
|
||||
*/
|
||||
public enableUser(requestParameters: UserV1alpha1ConsoleApiEnableUserRequest, options?: RawAxiosRequestConfig) {
|
||||
return UserV1alpha1ConsoleApiFp(this.configuration).enableUser(requestParameters.username, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current user detail
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
|
@ -242,6 +242,13 @@ core:
|
|||
actions:
|
||||
extension-point-settings: Extension settings
|
||||
user:
|
||||
operations:
|
||||
enable:
|
||||
title: Enable
|
||||
description: Are you sure you want to enable this user?
|
||||
disable:
|
||||
title: Disable
|
||||
description: Are you sure you want to disable this user? This user will not be able to log in after being disabled
|
||||
grant_permission_modal:
|
||||
roles_preview:
|
||||
all: The currently selected role contains all permissions
|
||||
|
@ -257,6 +264,8 @@ core:
|
|||
tooltip: Verified
|
||||
email_not_verified:
|
||||
tooltip: Not verified
|
||||
fields:
|
||||
disabled: Disabled
|
||||
role:
|
||||
editing_modal:
|
||||
fields:
|
||||
|
|
|
@ -1018,6 +1018,14 @@ core:
|
|||
title: Change password
|
||||
grant_permission:
|
||||
title: Grant permission
|
||||
enable:
|
||||
title: Enable
|
||||
description: Are you sure you want to enable this user?
|
||||
disable:
|
||||
title: Disable
|
||||
description: >-
|
||||
Are you sure you want to disable this user? This user will not be able
|
||||
to log in after being disabled
|
||||
filters:
|
||||
role:
|
||||
label: Role
|
||||
|
@ -1091,6 +1099,8 @@ core:
|
|||
tooltip: Verified
|
||||
email_not_verified:
|
||||
tooltip: Not verified
|
||||
fields:
|
||||
disabled: Disabled
|
||||
role:
|
||||
title: Roles
|
||||
common:
|
||||
|
|
|
@ -951,6 +951,12 @@ core:
|
|||
title: 修改密码
|
||||
grant_permission:
|
||||
title: 分配角色
|
||||
enable:
|
||||
title: 取消禁用
|
||||
description: 确定取消禁用该用户吗?
|
||||
disable:
|
||||
title: 禁用
|
||||
description: 确定禁用该用户吗?禁用之后该用户将无法登录系统
|
||||
filters:
|
||||
role:
|
||||
label: 角色
|
||||
|
@ -1021,6 +1027,8 @@ core:
|
|||
tooltip: 已验证
|
||||
email_not_verified:
|
||||
tooltip: 未验证
|
||||
fields:
|
||||
disabled: 已禁用
|
||||
role:
|
||||
title: 角色
|
||||
common:
|
||||
|
|
|
@ -936,6 +936,12 @@ core:
|
|||
title: 修改密碼
|
||||
grant_permission:
|
||||
title: 分配角色
|
||||
enable:
|
||||
title: 取消停用
|
||||
description: 確定要取消停用該用戶嗎?
|
||||
disable:
|
||||
title: 停用
|
||||
description: 確定要停用該用戶嗎?停用後該用戶將無法登入系統
|
||||
filters:
|
||||
role:
|
||||
label: 角色
|
||||
|
@ -1006,6 +1012,8 @@ core:
|
|||
tooltip: 已驗證
|
||||
email_not_verified:
|
||||
tooltip: 未驗證
|
||||
fields:
|
||||
disabled: 已停用
|
||||
role:
|
||||
title: 角色
|
||||
common:
|
||||
|
|
Loading…
Reference in New Issue