mirror of https://github.com/halo-dev/halo
Provide an endpoint to update user profile (#3067)
#### What type of PR is this? /kind feature /area core /milestone 2.1.x #### What this PR does / why we need it: Provide an endpoint to update current user profile instead of whole user data. ##### Request example ```bash curl -X 'PUT' \ 'http://localhost:8090/apis/api.console.halo.run/v1alpha1/users/-' \ -H 'accept: */*' \ -H 'Content-Type: */*' \ -d '{ "spec": { "displayName": "JohnNiang", "email": "johnniang@halo.run", "password": "xxx", "registeredAt": "2022-12-19T03:46:54.809770900Z", "twoFactorAuthEnabled": false, "disabled": false }, "status": { "permalink": "http://localhost:8090/authors/admin" }, "apiVersion": "v1alpha1", "kind": "User", "metadata": { "finalizers": [ "user-protection" ], "name": "admin", "annotations": { "rbac.authorization.halo.run/role-names": "[\"super-role\"]" }, "version": 3, "creationTimestamp": "2022-12-19T03:46:54.911951800Z" } }' ``` ##### Response example ```json { "spec": { "displayName": "JohnNiang", "email": "johnniang@halo.run", "password": "{bcrypt}$2a$10$IBV8/q7Q6Fj78Ls5AG1eBO0bCQ.rM6vli5pAVexf/gqu.hNfjJxaq", "registeredAt": "2022-12-19T03:46:54.809770900Z", "twoFactorAuthEnabled": false, "disabled": false }, "status": { "permalink": "http://localhost:8090/authors/admin" }, "apiVersion": "v1alpha1", "kind": "User", "metadata": { "finalizers": [ "user-protection" ], "name": "admin", "annotations": { "rbac.authorization.halo.run/role-names": "[\"super-role\"]" }, "version": 5, "creationTimestamp": "2022-12-19T03:46:54.911951800Z" } } ``` #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/3035 #### Does this PR introduce a user-facing change? ```release-note 提供更新当前登录用户信息功能 ```pull/3068/head^2
parent
da55532777
commit
313605d52c
|
@ -16,7 +16,9 @@ import java.util.stream.Collectors;
|
|||
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.NonNull;
|
||||
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.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
|
@ -52,6 +54,12 @@ public class UserEndpoint implements CustomEndpoint {
|
|||
.description("Get current user detail")
|
||||
.tag(tag)
|
||||
.response(responseBuilder().implementation(User.class)))
|
||||
.PUT("/users/-", this::updateProfile,
|
||||
builder -> builder.operationId("UpdateCurrentUser")
|
||||
.description("Update current user profile, but password.")
|
||||
.tag(tag)
|
||||
.requestBody(requestBodyBuilder().required(true).implementation(User.class))
|
||||
.response(responseBuilder().implementation(User.class)))
|
||||
.POST("/users/{name}/permissions", this::grantPermission,
|
||||
builder -> builder.operationId("GrantPermission")
|
||||
.description("Grant permissions to user")
|
||||
|
@ -89,6 +97,32 @@ public class UserEndpoint implements CustomEndpoint {
|
|||
.build();
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> updateProfile(ServerRequest request) {
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map(SecurityContext::getAuthentication)
|
||||
.map(Authentication::getName)
|
||||
.flatMap(currentUserName -> client.get(User.class, currentUserName))
|
||||
.flatMap(currentUser -> request.bodyToMono(User.class)
|
||||
.filter(user ->
|
||||
Objects.equals(user.getMetadata().getName(),
|
||||
currentUser.getMetadata().getName()))
|
||||
.switchIfEmpty(
|
||||
Mono.error(() -> new ServerWebInputException("Username didn't match.")))
|
||||
.map(user -> {
|
||||
var spec = currentUser.getSpec();
|
||||
var newSpec = user.getSpec();
|
||||
spec.setAvatar(newSpec.getAvatar());
|
||||
spec.setBio(newSpec.getBio());
|
||||
spec.setDisplayName(newSpec.getDisplayName());
|
||||
spec.setTwoFactorAuthEnabled(newSpec.getTwoFactorAuthEnabled());
|
||||
spec.setEmail(newSpec.getEmail());
|
||||
spec.setPhone(newSpec.getPhone());
|
||||
return currentUser;
|
||||
}))
|
||||
.flatMap(client::update)
|
||||
.flatMap(updatedUser -> ServerResponse.ok().bodyValue(updatedUser));
|
||||
}
|
||||
|
||||
Mono<ServerResponse> changePassword(ServerRequest request) {
|
||||
final var nameInPath = request.pathVariable("name");
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
|
|
|
@ -31,7 +31,7 @@ rules:
|
|||
- apiGroups: [ "api.console.halo.run" ]
|
||||
resources: [ "users" ]
|
||||
resourceNames: [ "-" ]
|
||||
verbs: [ "list", "get" ]
|
||||
verbs: [ "get", "update" ]
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: "Role"
|
||||
|
|
|
@ -98,6 +98,65 @@ class UserEndpointTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("UpdateProfile")
|
||||
class UpdateProfileTest {
|
||||
|
||||
@Test
|
||||
void shouldUpdateProfileCorrectly() {
|
||||
var currentUser = createUser("fake-user");
|
||||
var updatedUser = createUser("fake-user");
|
||||
var requestUser = createUser("fake-user");
|
||||
|
||||
when(client.get(User.class, "fake-user")).thenReturn(Mono.just(currentUser));
|
||||
when(client.update(currentUser)).thenReturn(Mono.just(updatedUser));
|
||||
|
||||
webClient.put().uri("/apis/api.console.halo.run/v1alpha1/users/-")
|
||||
.bodyValue(requestUser)
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody(User.class)
|
||||
.isEqualTo(updatedUser);
|
||||
|
||||
verify(client).get(User.class, "fake-user");
|
||||
verify(client).update(currentUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetErrorIfUsernameMismatch() {
|
||||
var currentUser = createUser("fake-user");
|
||||
var updatedUser = createUser("fake-user");
|
||||
var requestUser = createUser("another-fake-user");
|
||||
|
||||
when(client.get(User.class, "fake-user")).thenReturn(Mono.just(currentUser));
|
||||
when(client.update(currentUser)).thenReturn(Mono.just(updatedUser));
|
||||
|
||||
webClient.put().uri("/apis/api.console.halo.run/v1alpha1/users/-")
|
||||
.bodyValue(requestUser)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
|
||||
verify(client).get(User.class, "fake-user");
|
||||
verify(client, never()).update(currentUser);
|
||||
}
|
||||
|
||||
User createUser(String name) {
|
||||
var spec = new User.UserSpec();
|
||||
spec.setEmail("hi@halo.run");
|
||||
spec.setBio("Fake bio");
|
||||
spec.setDisplayName("Faker");
|
||||
spec.setPassword("fake-password");
|
||||
|
||||
var metadata = new Metadata();
|
||||
metadata.setName(name);
|
||||
|
||||
var user = new User();
|
||||
user.setSpec(spec);
|
||||
user.setMetadata(metadata);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("ChangePassword")
|
||||
class ChangePasswordTest {
|
||||
|
|
Loading…
Reference in New Issue