mirror of https://github.com/halo-dev/halo
Add an endpoint to grant permissions to user (#2239)
#### What type of PR is this? /kind feature /kind api-change /area core /milestone 2.0 #### What this PR does / why we need it: Add an endpoint to grant permissions to user. #### Which issue(s) this PR fixes: Fixes # #### Special notes for your reviewer: Test steps: 1. Start Halo 2. Check the initial password in the console log 3. Request <http://localhost:8090/webjars/swagger-ui/index.html> from browser and you will be redirected to login page 4. Input the username(admin) and the password you got just now 5. Grant permission as you wish #### Does this PR introduce a user-facing change? ```release-note None ```pull/2241/head
parent
de493ccb2c
commit
faae645e88
|
@ -1,6 +1,9 @@
|
|||
package run.halo.app.core.extension;
|
||||
|
||||
import static java.util.Arrays.compare;
|
||||
import static run.halo.app.core.extension.Role.GROUP;
|
||||
import static run.halo.app.core.extension.Role.KIND;
|
||||
import static run.halo.app.core.extension.Role.VERSION;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.List;
|
||||
|
@ -18,13 +21,17 @@ import run.halo.app.extension.GVK;
|
|||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@GVK(group = "",
|
||||
version = "v1alpha1",
|
||||
kind = "Role",
|
||||
@GVK(group = GROUP,
|
||||
version = VERSION,
|
||||
kind = KIND,
|
||||
plural = "roles",
|
||||
singular = "role")
|
||||
public class Role extends AbstractExtension {
|
||||
|
||||
public static final String GROUP = "";
|
||||
public static final String VERSION = "v1alpha1";
|
||||
public static final String KIND = "Role";
|
||||
|
||||
@Schema(required = true)
|
||||
List<PolicyRule> rules;
|
||||
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
package run.halo.app.core.extension;
|
||||
|
||||
import static run.halo.app.core.extension.RoleBinding.GROUP;
|
||||
import static run.halo.app.core.extension.RoleBinding.KIND;
|
||||
import static run.halo.app.core.extension.RoleBinding.VERSION;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import run.halo.app.extension.AbstractExtension;
|
||||
import run.halo.app.extension.ExtensionOperator;
|
||||
import run.halo.app.extension.GVK;
|
||||
import run.halo.app.extension.Metadata;
|
||||
|
||||
/**
|
||||
* RoleBinding references a role, but does not contain it.
|
||||
|
@ -20,13 +29,19 @@ import run.halo.app.extension.GVK;
|
|||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@GVK(group = "",
|
||||
version = "v1alpha1",
|
||||
kind = "RoleBinding",
|
||||
@GVK(group = GROUP,
|
||||
version = VERSION,
|
||||
kind = KIND,
|
||||
plural = "rolebindings",
|
||||
singular = "rolebinding")
|
||||
public class RoleBinding extends AbstractExtension {
|
||||
|
||||
public static final String GROUP = "";
|
||||
|
||||
public static final String VERSION = "v1alpha1";
|
||||
|
||||
public static final String KIND = "RoleBinding";
|
||||
|
||||
/**
|
||||
* Subjects holds references to the objects the role applies to.
|
||||
*/
|
||||
|
@ -91,5 +106,55 @@ public class RoleBinding extends AbstractExtension {
|
|||
* Defaults to "rbac.authorization.halo.run" for User and Group subjects.
|
||||
*/
|
||||
String apiGroup;
|
||||
|
||||
public static Predicate<Subject> isUser(String username) {
|
||||
return subject -> User.KIND.equals(subject.getKind())
|
||||
&& User.GROUP.equals(subject.getApiGroup())
|
||||
&& username.equals(subject.getName());
|
||||
}
|
||||
|
||||
public static Predicate<Subject> containsUser(Set<String> usernames) {
|
||||
return subject -> User.KIND.equals(subject.getKind())
|
||||
&& User.GROUP.equals(subject.apiGroup)
|
||||
&& usernames.contains(subject.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static RoleBinding create(String username, String roleName) {
|
||||
var metadata = new Metadata();
|
||||
metadata.setName(String.join("-", username, roleName, "binding"));
|
||||
|
||||
var roleRef = new RoleRef();
|
||||
roleRef.setKind(Role.KIND);
|
||||
roleRef.setName(roleName);
|
||||
roleRef.setApiGroup(Role.GROUP);
|
||||
|
||||
var subject = new Subject();
|
||||
subject.setKind(User.KIND);
|
||||
subject.setName(username);
|
||||
subject.setApiGroup(User.GROUP);
|
||||
|
||||
var binding = new RoleBinding();
|
||||
binding.setMetadata(metadata);
|
||||
binding.setRoleRef(roleRef);
|
||||
|
||||
// keep the subjects mutable
|
||||
var subjects = new LinkedList<Subject>();
|
||||
subjects.add(subject);
|
||||
|
||||
binding.setSubjects(subjects);
|
||||
return binding;
|
||||
}
|
||||
|
||||
public static Predicate<RoleBinding> containsUser(String username) {
|
||||
return ExtensionOperator.<RoleBinding>isNotDeleted().and(
|
||||
binding -> binding.getSubjects().stream()
|
||||
.anyMatch(Subject.isUser(username)));
|
||||
}
|
||||
|
||||
public static Predicate<RoleBinding> containsUser(Set<String> usernames) {
|
||||
return ExtensionOperator.<RoleBinding>isNotDeleted()
|
||||
.and(binding -> binding.getSubjects().stream()
|
||||
.anyMatch(Subject.containsUser(usernames)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package run.halo.app.core.extension;
|
||||
|
||||
import static run.halo.app.core.extension.User.GROUP;
|
||||
import static run.halo.app.core.extension.User.KIND;
|
||||
import static run.halo.app.core.extension.User.VERSION;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
@ -17,13 +21,17 @@ import run.halo.app.extension.GVK;
|
|||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@GVK(group = "",
|
||||
version = "v1alpha1",
|
||||
kind = "User",
|
||||
@GVK(group = GROUP,
|
||||
version = VERSION,
|
||||
kind = KIND,
|
||||
singular = "user",
|
||||
plural = "users")
|
||||
public class User extends AbstractExtension {
|
||||
|
||||
public static final String GROUP = "";
|
||||
public static final String VERSION = "v1alpha1";
|
||||
public static final String KIND = "User";
|
||||
|
||||
@Schema(required = true)
|
||||
private UserSpec spec;
|
||||
|
||||
|
|
|
@ -1,15 +1,26 @@
|
|||
package run.halo.app.core.extension.endpoint;
|
||||
|
||||
import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder;
|
||||
import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
|
||||
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
|
||||
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.Role;
|
||||
import run.halo.app.core.extension.RoleBinding;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.exception.ExtensionNotFoundException;
|
||||
|
@ -35,13 +46,76 @@ public class UserEndpoint implements CustomEndpoint {
|
|||
.bodyValue(user));
|
||||
}
|
||||
|
||||
Mono<ServerResponse> grantPermission(ServerRequest request) {
|
||||
var username = request.pathVariable("name");
|
||||
return request.bodyToMono(GrantRequest.class)
|
||||
.switchIfEmpty(Mono.error(() -> new ServerWebInputException("Request body is empty")))
|
||||
.flatMap(grant -> {
|
||||
// preflight check
|
||||
client.fetch(User.class, username)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
"User " + username + " was not found"));
|
||||
|
||||
grant.roles.forEach(roleName -> client.fetch(Role.class, roleName)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
|
||||
"Role " + roleName + " was not found")));
|
||||
|
||||
var bindings =
|
||||
client.list(RoleBinding.class, RoleBinding.containsUser(username), null);
|
||||
var bindingToUpdate = new HashSet<RoleBinding>();
|
||||
var bindingToDelete = new HashSet<RoleBinding>();
|
||||
var existingRoles = new HashSet<String>();
|
||||
bindings.forEach(binding -> {
|
||||
var roleName = binding.getRoleRef().getName();
|
||||
if (grant.roles.contains(roleName)) {
|
||||
existingRoles.add(roleName);
|
||||
return;
|
||||
}
|
||||
binding.getSubjects().removeIf(RoleBinding.Subject.isUser(username));
|
||||
if (CollectionUtils.isEmpty(binding.getSubjects())) {
|
||||
// remove it if subjects is empty
|
||||
bindingToDelete.add(binding);
|
||||
} else {
|
||||
bindingToUpdate.add(binding);
|
||||
}
|
||||
});
|
||||
|
||||
bindingToUpdate.forEach(client::update);
|
||||
bindingToDelete.forEach(client::delete);
|
||||
|
||||
// remove existing roles
|
||||
var roles = new HashSet<>(grant.roles);
|
||||
roles.removeAll(existingRoles);
|
||||
roles.stream()
|
||||
.map(roleName -> RoleBinding.create(username, roleName))
|
||||
.forEach(client::create);
|
||||
|
||||
return ServerResponse.ok().build();
|
||||
});
|
||||
}
|
||||
|
||||
record GrantRequest(Set<String> roles) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> endpoint() {
|
||||
var tag = "api.halo.run/v1alpha1/User";
|
||||
return SpringdocRouteBuilder.route()
|
||||
.GET("/users/-", this::me, builder -> builder.operationId("GetCurrentUserDetail")
|
||||
.description("Get current user detail")
|
||||
.tag("api.halo.run/v1alpha1/User")
|
||||
.tag(tag)
|
||||
.response(responseBuilder().implementation(User.class)))
|
||||
.POST("/users/{name}/permissions", this::grantPermission,
|
||||
builder -> builder.operationId("GrantPermission")
|
||||
.description("Grant permissions to user")
|
||||
.tag(tag)
|
||||
.parameter(parameterBuilder().in(ParameterIn.PATH).name("name")
|
||||
.description("User name")
|
||||
.required(true))
|
||||
.requestBody(
|
||||
requestBodyBuilder().required(true).implementation(GrantRequest.class))
|
||||
.response(responseBuilder().implementation(User.class)))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package run.halo.app.core.extension.reconciler;
|
||||
|
||||
import static run.halo.app.core.extension.RoleBinding.containsUser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import run.halo.app.core.extension.Role;
|
||||
import run.halo.app.core.extension.RoleBinding;
|
||||
import run.halo.app.core.extension.RoleBinding.Subject;
|
||||
import run.halo.app.core.extension.User;
|
||||
|
@ -32,7 +33,7 @@ public class RoleBindingReconciler implements Reconciler {
|
|||
client.fetch(RoleBinding.class, request.name()).ifPresent(roleBinding -> {
|
||||
// get all usernames;
|
||||
var usernames = roleBinding.getSubjects().stream()
|
||||
.filter(subject -> "User".equals(subject.getKind()))
|
||||
.filter(subject -> User.KIND.equals(subject.getKind()))
|
||||
.map(Subject::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
@ -44,7 +45,7 @@ public class RoleBindingReconciler implements Reconciler {
|
|||
var roleNames = bindings.get().stream()
|
||||
.filter(containsUser(username))
|
||||
.map(RoleBinding::getRoleRef)
|
||||
.filter(roleRef -> Objects.equals(roleRef.getKind(), "Role"))
|
||||
.filter(roleRef -> Objects.equals(roleRef.getKind(), Role.KIND))
|
||||
.map(RoleBinding.RoleRef::getName)
|
||||
.sorted()
|
||||
// we have to use LinkedHashSet below to make sure the sorted above functional
|
||||
|
@ -68,17 +69,4 @@ public class RoleBindingReconciler implements Reconciler {
|
|||
return new Result(false, null);
|
||||
}
|
||||
|
||||
Predicate<RoleBinding> containsUser(String username) {
|
||||
return roleBinding -> roleBinding.getMetadata().getDeletionTimestamp() == null
|
||||
&& roleBinding.getSubjects().stream()
|
||||
.anyMatch(subject -> "User".equals(subject.getKind())
|
||||
&& username.equals(subject.getName()));
|
||||
}
|
||||
|
||||
Predicate<RoleBinding> containsUser(Set<String> usernames) {
|
||||
return roleBinding -> roleBinding.getMetadata().getDeletionTimestamp() == null
|
||||
&& roleBinding.getSubjects().stream()
|
||||
.anyMatch(subject -> "User".equals(subject.getKind())
|
||||
&& usernames.contains(subject.getName()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package run.halo.app.extension;
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.function.Predicate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -91,4 +92,7 @@ public interface ExtensionOperator {
|
|||
return GroupVersionKind.fromAPIVersionAndKind(getApiVersion(), getKind());
|
||||
}
|
||||
|
||||
static <T extends ExtensionOperator> Predicate<T> isNotDeleted() {
|
||||
return ext -> ext.getMetadata().getDeletionTimestamp() == null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package run.halo.app.core.extension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import run.halo.app.extension.Metadata;
|
||||
|
||||
class RoleBindingTest {
|
||||
|
||||
@Test
|
||||
void shouldContainUser() {
|
||||
var subject = new RoleBinding.Subject();
|
||||
subject.setName("fake-name");
|
||||
subject.setApiGroup("");
|
||||
subject.setKind("User");
|
||||
|
||||
var binding = new RoleBinding();
|
||||
binding.setMetadata(new Metadata());
|
||||
binding.setSubjects(List.of(subject));
|
||||
assertTrue(RoleBinding.containsUser("fake-name").test(binding));
|
||||
assertFalse(RoleBinding.containsUser("non-exist-fake-name").test(binding));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotContainUserWhenBindingIsDeleted() {
|
||||
var subject = new RoleBinding.Subject();
|
||||
subject.setName("fake-name");
|
||||
subject.setApiGroup("");
|
||||
subject.setKind("User");
|
||||
|
||||
var binding = new RoleBinding();
|
||||
var metadata = new Metadata();
|
||||
metadata.setDeletionTimestamp(Instant.now());
|
||||
binding.setMetadata(metadata);
|
||||
binding.setSubjects(List.of(subject));
|
||||
assertFalse(RoleBinding.containsUser("fake-name").test(binding));
|
||||
assertFalse(RoleBinding.containsUser("non-exist-fake-name").test(binding));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,22 @@
|
|||
package run.halo.app.core.extension.endpoint;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
|
||||
|
@ -15,6 +26,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import run.halo.app.core.extension.Role;
|
||||
import run.halo.app.core.extension.RoleBinding;
|
||||
import run.halo.app.core.extension.User;
|
||||
import run.halo.app.core.extension.service.RoleService;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
|
@ -44,6 +56,8 @@ class UserEndpointTest {
|
|||
var role = new Role();
|
||||
role.setRules(List.of(rule));
|
||||
when(roleService.getRole(anyString())).thenReturn(role);
|
||||
// prevent from initializing the super admin.
|
||||
when(client.fetch(User.class, "admin")).thenReturn(Optional.of(mock(User.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -70,4 +84,99 @@ class UserEndpointTest {
|
|||
.expectBody(User.class)
|
||||
.isEqualTo(user);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class GrantPermissionEndpointTest {
|
||||
|
||||
@Test
|
||||
@WithMockUser("fake-user")
|
||||
void shouldGetBadRequestIfRequestBodyIsEmpty() {
|
||||
webClient.post().uri("/apis/api.halo.run/v1alpha1/users/fake-user/permissions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.exchange()
|
||||
.expectStatus().isBadRequest();
|
||||
|
||||
// Why one more time to verify? Because the SuperAdminInitializer will fetch admin user.
|
||||
verify(client, never()).fetch(same(User.class), eq("fake-user"));
|
||||
verify(client, never()).fetch(same(Role.class), eq("fake-role"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser("fake-user")
|
||||
void shouldGetNotFoundIfUserNotFound() {
|
||||
when(client.fetch(User.class, "fake-user")).thenReturn(Optional.empty());
|
||||
when(client.fetch(Role.class, "fake-role")).thenReturn(Optional.of(mock(Role.class)));
|
||||
|
||||
webClient.post().uri("/apis/api.halo.run/v1alpha1/users/fake-user/permissions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(new UserEndpoint.GrantRequest(Set.of("fake-role")))
|
||||
.exchange()
|
||||
.expectStatus().isNotFound();
|
||||
|
||||
verify(client, times(1)).fetch(same(User.class), eq("fake-user"));
|
||||
verify(client, never()).fetch(same(Role.class), eq("fake-role"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser("fake-user")
|
||||
void shouldGetNotFoundIfRoleNotFound() {
|
||||
when(client.fetch(User.class, "fake-user")).thenReturn(Optional.of(mock(User.class)));
|
||||
when(client.fetch(Role.class, "fake-role")).thenReturn(Optional.empty());
|
||||
|
||||
webClient.post().uri("/apis/api.halo.run/v1alpha1/users/fake-user/permissions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(new UserEndpoint.GrantRequest(Set.of("fake-role")))
|
||||
.exchange()
|
||||
.expectStatus().isNotFound();
|
||||
|
||||
verify(client, times(1)).fetch(same(User.class), eq("fake-user"));
|
||||
verify(client, times(1)).fetch(same(Role.class), eq("fake-role"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser("fake-user")
|
||||
void shouldCreateRoleBindingIfNotExist() {
|
||||
when(client.fetch(User.class, "fake-user")).thenReturn(Optional.of(mock(User.class)));
|
||||
var role = mock(Role.class);
|
||||
when(client.fetch(Role.class, "fake-role")).thenReturn(Optional.of(role));
|
||||
|
||||
webClient.post().uri("/apis/api.halo.run/v1alpha1/users/fake-user/permissions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(new UserEndpoint.GrantRequest(Set.of("fake-role")))
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
|
||||
verify(client, times(1)).fetch(same(User.class), eq("fake-user"));
|
||||
verify(client, times(1)).fetch(same(Role.class), eq("fake-role"));
|
||||
verify(client, times(1)).create(RoleBinding.create("fake-user", "fake-role"));
|
||||
verify(client, never()).update(isA(RoleBinding.class));
|
||||
verify(client, never()).delete(isA(RoleBinding.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser("fake-user")
|
||||
void shouldDeleteRoleBindingIfNotProvided() {
|
||||
when(client.fetch(User.class, "fake-user")).thenReturn(Optional.of(mock(User.class)));
|
||||
var role = mock(Role.class);
|
||||
when(client.fetch(Role.class, "fake-role")).thenReturn(Optional.of(role));
|
||||
var roleBinding = RoleBinding.create("fake-user", "non-provided-fake-role");
|
||||
when(client.list(same(RoleBinding.class), any(), any())).thenReturn(
|
||||
List.of(roleBinding));
|
||||
|
||||
webClient.post().uri("/apis/api.halo.run/v1alpha1/users/fake-user/permissions")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(new UserEndpoint.GrantRequest(Set.of("fake-role")))
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
|
||||
verify(client, times(1)).fetch(same(User.class), eq("fake-user"));
|
||||
verify(client, times(1)).fetch(same(Role.class), eq("fake-role"));
|
||||
verify(client, times(1)).create(RoleBinding.create("fake-user", "fake-role"));
|
||||
verify(client, times(1)).delete(argThat(binding ->
|
||||
binding.getMetadata().getName().equals(roleBinding.getMetadata().getName())));
|
||||
verify(client, never()).update(isA(RoleBinding.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -137,6 +137,10 @@ class RoleBindingReconcilerTest {
|
|||
var bindingName = "fake-binding-name";
|
||||
var userName = "fake-user-name";
|
||||
var subject = mock(Subject.class);
|
||||
when(subject.getKind()).thenReturn("User");
|
||||
when(subject.getName()).thenReturn(userName);
|
||||
when(subject.getApiGroup()).thenReturn("");
|
||||
|
||||
var user = mock(User.class);
|
||||
var userMetadata = mock(Metadata.class);
|
||||
var binding = createRoleBinding("Role", "fake-role", false, subject);
|
||||
|
@ -145,8 +149,6 @@ class RoleBindingReconcilerTest {
|
|||
.thenReturn(Optional.of(binding));
|
||||
when(binding.getSubjects()).thenReturn(List.of(subject));
|
||||
|
||||
when(subject.getKind()).thenReturn("User");
|
||||
when(subject.getName()).thenReturn(userName);
|
||||
when(client.fetch(User.class, userName)).thenReturn(Optional.of(user));
|
||||
when(user.getMetadata()).thenReturn(userMetadata);
|
||||
var bindings = List.of(
|
||||
|
@ -182,6 +184,7 @@ class RoleBindingReconcilerTest {
|
|||
var metadata = mock(Metadata.class);
|
||||
lenient().when(roleRef.getKind()).thenReturn(roleRefKind);
|
||||
lenient().when(roleRef.getName()).thenReturn(roleRefName);
|
||||
lenient().when(roleRef.getApiGroup()).thenReturn("");
|
||||
lenient().when(metadata.getDeletionTimestamp()).thenReturn(deleting ? Instant.now() : null);
|
||||
lenient().when(binding.getRoleRef()).thenReturn(roleRef);
|
||||
lenient().when(binding.getMetadata()).thenReturn(metadata);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Instant;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ExtensionOperatorTest {
|
||||
|
||||
@Test
|
||||
void testIsNotDeleted() {
|
||||
var ext = mock(ExtensionOperator.class);
|
||||
var metadata = mock(Metadata.class);
|
||||
when(metadata.getDeletionTimestamp()).thenReturn(null);
|
||||
when(ext.getMetadata()).thenReturn(metadata);
|
||||
|
||||
assertTrue(ExtensionOperator.isNotDeleted().test(ext));
|
||||
|
||||
when(metadata.getDeletionTimestamp()).thenReturn(Instant.now());
|
||||
assertFalse(ExtensionOperator.isNotDeleted().test(ext));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue