diff --git a/control-panel/src/main/java/ru/dragonestia/picker/cp/component/AccountList.java b/control-panel/src/main/java/ru/dragonestia/picker/cp/component/AccountList.java
index c3a2533..8f1c0b1 100644
--- a/control-panel/src/main/java/ru/dragonestia/picker/cp/component/AccountList.java
+++ b/control-panel/src/main/java/ru/dragonestia/picker/cp/component/AccountList.java
@@ -73,7 +73,9 @@ public class AccountList extends VerticalLayout implements RefreshableTable {
var button = new Button("Manage", VaadinIcon.COG_O.create());
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
button.addClickListener(event -> {
-
+ getUI().ifPresent(ui -> {
+ ui.navigate("/admin/accounts/" + account.getUsername());
+ });
});
layout.add(button);
}
@@ -171,7 +173,7 @@ public class AccountList extends VerticalLayout implements RefreshableTable {
}
if (password.length() < 5 || password.length() > 32) {
- Notifications.error("Invalid username length. Valid is 5-32");
+ Notifications.error("Invalid password length. Valid is 5-32");
return;
}
@@ -192,7 +194,7 @@ public class AccountList extends VerticalLayout implements RefreshableTable {
refresh();
}
- private static class PermissionCheckBox extends Checkbox {
+ public static class PermissionCheckBox extends Checkbox {
private final Permission.Enum option;
diff --git a/control-panel/src/main/java/ru/dragonestia/picker/cp/page/AccountDetailsPage.java b/control-panel/src/main/java/ru/dragonestia/picker/cp/page/AccountDetailsPage.java
new file mode 100644
index 0000000..6da64c8
--- /dev/null
+++ b/control-panel/src/main/java/ru/dragonestia/picker/cp/page/AccountDetailsPage.java
@@ -0,0 +1,138 @@
+package ru.dragonestia.picker.cp.page;
+
+import com.vaadin.flow.component.AbstractField;
+import com.vaadin.flow.component.Html;
+import com.vaadin.flow.component.Unit;
+import com.vaadin.flow.component.button.Button;
+import com.vaadin.flow.component.button.ButtonVariant;
+import com.vaadin.flow.component.html.H2;
+import com.vaadin.flow.component.html.H3;
+import com.vaadin.flow.component.html.Hr;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.component.textfield.PasswordField;
+import com.vaadin.flow.router.BeforeEnterEvent;
+import com.vaadin.flow.router.BeforeEnterObserver;
+import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.Route;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.security.RolesAllowed;
+import lombok.RequiredArgsConstructor;
+import ru.dragonestia.picker.api.impl.RoomPickerClient;
+import ru.dragonestia.picker.api.model.account.IAccount;
+import ru.dragonestia.picker.cp.component.AccountList;
+import ru.dragonestia.picker.cp.component.Notifications;
+import ru.dragonestia.picker.cp.model.Permission;
+import ru.dragonestia.picker.cp.service.SecurityService;
+import ru.dragonestia.picker.cp.util.RouteParamsExtractor;
+
+import java.util.ArrayList;
+
+@RequiredArgsConstructor
+@RolesAllowed("ADMIN")
+@PageTitle("Account details")
+@Route(value = "/admin/accounts/:accountId", layout = MainLayout.class)
+public class AccountDetailsPage extends VerticalLayout implements BeforeEnterObserver {
+
+ private final SecurityService securityService;
+ private final RouteParamsExtractor paramsExtractor;
+
+ private RoomPickerClient client;
+ private IAccount account;
+
+ @PostConstruct
+ void postConstruct() {
+ client = securityService.getAuthenticatedAccount().getClient();
+ }
+
+ @Override
+ public void beforeEnter(BeforeEnterEvent event) {
+ account = paramsExtractor.extractAccount(event);
+
+ init();
+ }
+
+ private void init() {
+ add(new H2("Account management"));
+ add(new Html("Username: %s".formatted(account.getUsername())));
+
+ add(new Hr());
+ add(new H3("Change password"));
+ createChangePassword();
+
+ add(new Hr());
+ add(new H3("Update permissions"));
+ createEditPermissions();
+
+ add(new Hr());
+ add(new H3("Delete account"));
+ add(createDeleteAccountButton());
+ }
+
+ private void createChangePassword() {
+ var newPassword = new PasswordField("New password");
+ newPassword.setMinWidth(25, Unit.PERCENTAGE);
+ add(newPassword);
+
+ var confirmPassword = new PasswordField("Confirm new password");
+ confirmPassword.setMinWidth(25, Unit.PERCENTAGE);
+ add(confirmPassword);
+
+ var submit = new Button("Submit", event -> {
+ var pass = newPassword.getValue();
+ var confirm = confirmPassword.getValue();
+
+ if (pass.length() < 5 || pass.length() > 32) {
+ Notifications.error("Invalid password length. Valid is 5-32");
+ return;
+ }
+
+ if (!pass.equals(confirm)) {
+ Notifications.error("Passwords are not equals");
+ return;
+ }
+
+ //TODO: change password
+ Notifications.success("Password successfully changed!");
+ newPassword.setValue("");
+ confirmPassword.setValue("");
+ });
+ submit.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
+ add(submit);
+ }
+
+ private void createEditPermissions() {
+ var permissionsList = new ArrayList();
+ for (var permission: Permission.Enum.values()) {
+ var comp = new AccountList.PermissionCheckBox(permission);
+ comp.setValue(account.getPermissions().contains(permission.name()));
+ permissionsList.add(comp);
+ add(comp);
+ }
+
+ var button = new Button("Update permissions", event -> {
+ var permissions = permissionsList.stream()
+ .filter(AbstractField::getValue)
+ .map(AccountList.PermissionCheckBox::getOption)
+ .map(Enum::name)
+ .toList();
+
+ client.getAccountRepository().setPermissions(account, permissions);
+ Notifications.success("Permissions successfully changed!");
+ });
+ button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
+ add(button);
+ }
+
+ private Button createDeleteAccountButton() {
+ var button = new Button("Delete this account", event -> {
+ client.getAccountRepository().removeAccount(account);
+ Notifications.warn("Account '%s' was deleted.".formatted(account.getUsername()));
+
+ getUI().ifPresent(ui -> {
+ ui.navigate("/admin/accounts");
+ });
+ });
+ button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
+ return button;
+ }
+}
diff --git a/control-panel/src/main/java/ru/dragonestia/picker/cp/util/RouteParamsExtractor.java b/control-panel/src/main/java/ru/dragonestia/picker/cp/util/RouteParamsExtractor.java
index e0205a8..ff9e758 100644
--- a/control-panel/src/main/java/ru/dragonestia/picker/cp/util/RouteParamsExtractor.java
+++ b/control-panel/src/main/java/ru/dragonestia/picker/cp/util/RouteParamsExtractor.java
@@ -6,6 +6,7 @@ import org.springframework.stereotype.Component;
import ru.dragonestia.picker.api.exception.NodeNotFoundException;
import ru.dragonestia.picker.api.exception.RoomNotFoundException;
import ru.dragonestia.picker.api.impl.RoomPickerClient;
+import ru.dragonestia.picker.api.model.account.IAccount;
import ru.dragonestia.picker.api.model.node.INode;
import ru.dragonestia.picker.api.model.room.IRoom;
import ru.dragonestia.picker.api.model.user.IUser;
@@ -37,4 +38,9 @@ public class RouteParamsExtractor {
var userId = UserIdentifier.of(e.getRouteParameters().get("userId").orElseThrow(RuntimeException::new));
return client.getUserRepository().findUserById(FindUserById.withAllDetails(userId));
}
+
+ public IAccount extractAccount(BeforeEnterEvent e) {
+ var accountId = e.getRouteParameters().get("accountId").orElseThrow(RuntimeException::new);
+ return client.getAccountRepository().findAccountByUsername(accountId).orElseThrow(RuntimeException::new);
+ }
}
diff --git a/server/src/main/java/ru/dragonestia/picker/controller/AccountsController.java b/server/src/main/java/ru/dragonestia/picker/controller/AccountsController.java
index c4da1a9..62d0a39 100644
--- a/server/src/main/java/ru/dragonestia/picker/controller/AccountsController.java
+++ b/server/src/main/java/ru/dragonestia/picker/controller/AccountsController.java
@@ -49,11 +49,12 @@ public class AccountsController {
}
@PostMapping
- ResponseAccount registerAccount(@RequestParam String username, @RequestParam String password, @RequestParam String permissions) {
+ ResponseAccount registerAccount(@RequestParam String username, @RequestParam String password, @RequestParam(defaultValue = "") String permissions) {
var account = accountService.createNewAccount(username, password);
var authorities = new HashSet();
for (var permStr : permissions.split(",")) {
+ if (permStr.isBlank()) continue;
try {
var perm = Permission.valueOf(permStr);
authorities.add(perm);
@@ -69,11 +70,12 @@ public class AccountsController {
}
@PutMapping("/{accountId}")
- ResponseEntity> updatePermissions(@PathVariable String accountId, @RequestParam String permissions) {
+ ResponseEntity> updatePermissions(@PathVariable String accountId, @RequestParam(defaultValue = "") String permissions) {
var account = accountService.findAccount(accountId).orElseThrow(() -> new AccountDoesNotExistsException(accountId));
var authorities = new HashSet();
for (var permStr : permissions.split(",")) {
+ if (permStr.isBlank()) continue;
try {
var perm = Permission.valueOf(permStr);
authorities.add(perm);