Merge branch 'main' into develop/0.2.0

This commit is contained in:
Thomas Peetz
2025-04-30 19:32:42 +02:00
93 changed files with 4219 additions and 218 deletions
@@ -18,36 +18,36 @@ public class AdminConstants {
public static final String ADMIN_TITLE = "Verwaltung";
public static final String AUTHORIZATION = "Berechtigungen";
public static final String AUTHORIZATION_ROUTE = "/admin/authorization";
public static final String ASSIGNMENT_ROUTE = "/admin/assignment";
public static final String DATA = "Daten";
public static final String METADATA_ROUTE = "admin/metadata";
public static final String ROLE = "Rollen";
public static final String ROLE_ROUTE = "/admin/role";
public static final String USER = "Benutzer";
public static final String USER_ROUTE = "/admin/user";
public static final String PERMISSION = "Berechtigungen";
public static final String PERMISSION_ROUTE = "/admin/permission";
public static final String PROFILE = "Benutzer";
public static final String PROFILE_ROUTE = "/admin/profile";
public static final String ADMIN = "admin";
public static final String ADMIN_ROUTE = "/admin";
public static RouterLink getUserNavigation() {
return new RouterLink(USER, UserView.class);
public static RouterLink getProfileNavigation() {
return new RouterLink(PROFILE, ProfileView.class);
}
public static RouterLink getRoleNavigation() {
return new RouterLink(ROLE, RoleView.class);
public static RouterLink getPermissionNavigation() {
return new RouterLink(PERMISSION, PermissionView.class);
}
public static RouterLink getAuthorizationNavigation() {
return new RouterLink(AUTHORIZATION, AuthorizationView.class);
return new RouterLink(AUTHORIZATION, AssignmentView.class);
}
public static SideNavItem getAdminNavigation() {
SideNavItem administration = new SideNavItem(ADMIN_TITLE, USER_ROUTE, VaadinIcon.GROUP.create());
administration.addItem(new SideNavItem(USER, USER_ROUTE, VaadinIcon.USERS.create()));
administration.addItem(new SideNavItem(ROLE, RoleView.class));
SideNavItem data = new SideNavItem(DATA, AUTHORIZATION_ROUTE, VaadinIcon.DATABASE.create());
SideNavItem administration = new SideNavItem(ADMIN_TITLE, PROFILE_ROUTE, VaadinIcon.GROUP.create());
administration.addItem(new SideNavItem(PROFILE, PROFILE_ROUTE, VaadinIcon.USERS.create()));
administration.addItem(new SideNavItem(PERMISSION, PermissionView.class));
SideNavItem data = new SideNavItem(DATA, ASSIGNMENT_ROUTE, VaadinIcon.DATABASE.create());
data.addItem(new SideNavItem(ComicConstants.COMICWORK, ComicWorkView.class));
data.addItem(new SideNavItem(MediaConstants.MEDIAACTORFILE, MediaActorFileView.class));
data.addItem(new SideNavItem(AUTHORIZATION, AuthorizationView.class));
data.addItem(new SideNavItem(AUTHORIZATION, AssignmentView.class));
data.addItem(new SideNavItem("Data Import", ModuleDataView.class));
data.addItem(new SideNavItem("Meta Data", MetaDataView.class));
administration.addItem(data);
@@ -1,9 +1,7 @@
package de.thpeetz.kontor.admin;
import de.thpeetz.kontor.admin.data.*;
import de.thpeetz.kontor.admin.repository.AuthorizationMatrixRepository;
import de.thpeetz.kontor.admin.repository.MailAccountRepository;
import de.thpeetz.kontor.admin.repository.UserRepository;
import de.thpeetz.kontor.admin.repository.*;
import de.thpeetz.kontor.admin.services.AdminService;
import de.thpeetz.kontor.admin.services.MetaDataService;
import de.thpeetz.kontor.mailclient.data.MailAccount;
@@ -23,10 +21,10 @@ public class SetupModuleAdmin implements ApplicationListener<ContextRefreshedEve
boolean alreadySetup = false;
@Autowired
private UserRepository userRepository;
private ProfileRepository profileRepository;
@Autowired
private AuthorizationMatrixRepository authorizationMatrixRepository;
private AssignmentRepository assignmentRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@@ -50,13 +48,13 @@ public class SetupModuleAdmin implements ApplicationListener<ContextRefreshedEve
}
// Create initial roles and users
Role adminRole = adminService.addRole("ROLE_ADMIN");
Role userRole = adminService.addRole("ROLE_USER");
List<User> users = userRepository.findAll();
if (users.isEmpty()) {
User adminUser = initAdminUser();
initMatrix(adminRole, adminUser);
initMatrix(userRole, adminUser);
Permission adminPermission = adminService.addPermission("ROLE_ADMIN");
Permission userPermission = adminService.addPermission("ROLE_USER");
List<Profile> profiles = profileRepository.findAll();
if (profiles.isEmpty()) {
Profile adminProfile = initAdminUser();
initAssignments(adminPermission, adminProfile);
initAssignments(userPermission, adminProfile);
}
log.info("MailProperties: {}", mailProperties);
initMail(mailProperties);
@@ -98,32 +96,32 @@ public class SetupModuleAdmin implements ApplicationListener<ContextRefreshedEve
}
}
private void initMatrix(Role role, User user) {
log.info("initMatrix: Role {} for User {}", role.getName(), user.getUserName());
Collection<AuthorizationMatrix> configuredRoles = authorizationMatrixRepository.findByUser(user);
if (configuredRoles.stream().anyMatch(matrix -> matrix.getRole().getId().equals(role.getId()))) {
log.info("Role {} already defined", role.getName());
private void initAssignments(Permission permission, Profile profile) {
log.info("initAssignments: Permission {} for Profile {}", permission.getName(), profile.getUserName());
Collection<Assignment> configuredRoles = assignmentRepository.findByProfile(profile);
if (configuredRoles.stream().anyMatch(assignment -> assignment.getPermission().getId().equals(permission.getId()))) {
log.info("Permission {} already defined", permission.getName());
} else {
log.info("add Role {} to User {}", role.getName(), user.getUserName());
final AuthorizationMatrix adminMatrix = new AuthorizationMatrix();
adminMatrix.setUser(user);
adminMatrix.setRole(role);
authorizationMatrixRepository.save(adminMatrix);
log.info("add Permission {} to Profile {}", permission.getName(), profile.getUserName());
final Assignment assignment = new Assignment();
assignment.setProfile(profile);
assignment.setPermission(permission);
assignmentRepository.save(assignment);
}
}
private User initAdminUser() {
private Profile initAdminUser() {
log.info("initAdminUser");
User admin = userRepository.findByUserName("admin");
Profile admin = profileRepository.findByUserName("admin");
if (admin == null) {
log.info("User admin not found, will be created.");
admin = new User();
log.info("Profile admin not found, will be created.");
admin = new Profile();
admin.setFirstName("Admin");
admin.setLastName("Administrator");
admin.setUserName("admin");
admin.setEmail("admin@example.org");
admin.setPassword(passwordEncoder.encode("admin"));
userRepository.save(admin);
profileRepository.save(admin);
}
return admin;
}
@@ -348,17 +346,17 @@ public class SetupModuleAdmin implements ApplicationListener<ContextRefreshedEve
metaDataService.getColumn(cardTable, "card_set_id", "card_set_id", "TEXT", null, 7, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(cardTable, "rooster_id", "rooster_id", "TEXT", null, 8, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(cardTable, "vendor_id", "vendor_id", "TEXT", null, 9, Boolean.FALSE, "", Boolean.FALSE, null);
MetaDataTable userTable = metaDataService.getTable("user");
metaDataService.getColumn(userTable, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "last_modified_date", "modified", "TIMESTAMP", null, 3, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "version", "version", "LONG", null, 4, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "first_name", "first_name", "TEXT", null, 5, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "last_name", "last_name", "TEXT", null, 6, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "user_name", "user_name", "TEXT", "UNIQUE", 7, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "email", "email", "TEXT", null, 8, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "password", "password", "TEXT", null, 9, Boolean.FALSE, "Password", Boolean.FALSE, null);
metaDataService.getColumn(userTable, "enabled", "enabled", "BOOLEAN", null, 10, Boolean.TRUE, "", Boolean.TRUE, null);
MetaDataTable profileTable = metaDataService.getTable("profile");
metaDataService.getColumn(profileTable, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "last_modified_date", "modified", "TIMESTAMP", null, 3, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "version", "version", "LONG", null, 4, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "first_name", "first_name", "TEXT", null, 5, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "last_name", "last_name", "TEXT", null, 6, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "user_name", "user_name", "TEXT", "UNIQUE", 7, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "email", "email", "TEXT", null, 8, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "password", "password", "TEXT", null, 9, Boolean.FALSE, "Password", Boolean.FALSE, null);
metaDataService.getColumn(profileTable, "enabled", "enabled", "BOOLEAN", null, 10, Boolean.TRUE, "", Boolean.TRUE, null);
MetaDataTable tokenTable = metaDataService.getTable("token");
metaDataService.getColumn(tokenTable, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(tokenTable, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
@@ -368,20 +366,20 @@ public class SetupModuleAdmin implements ApplicationListener<ContextRefreshedEve
metaDataService.getColumn(tokenTable, "name", "name", "TEXT", null, 6, Boolean.TRUE, "Name", Boolean.FALSE, null);
metaDataService.getColumn(tokenTable, "last_used_date", "used", "TIMESTAMP", null, 7, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(tokenTable, "enabled", "enabled", "BOOLEAN", null, 8, Boolean.TRUE, "", Boolean.TRUE, "Enabled");
metaDataService.getColumn(tokenTable, "user_id", "user_id", "TEXT", null, 9, Boolean.TRUE, "User", Boolean.FALSE, null, "user_name");
MetaDataTable roleTable = metaDataService.getTable("role");
metaDataService.getColumn(roleTable, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(roleTable, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(roleTable, "last_modified_date", "modified", "TIMESTAMP", null, 3, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(roleTable, "version", "version", "LONG", null, 4, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(roleTable, "name", "name", "TEXT", null, 5, Boolean.TRUE, "", Boolean.FALSE, null);
MetaDataTable authorizationMatrix = metaDataService.getTable("authorization_matrix");
metaDataService.getColumn(authorizationMatrix, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(authorizationMatrix, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(authorizationMatrix, "last_modified_date", "modified", "TIMESTAMP", null, 3, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(authorizationMatrix, "version", "version", "LONG", null, 4, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(authorizationMatrix, "user_id", "user_id", "TEXT", null, 5, Boolean.TRUE, "User", Boolean.FALSE, null, "user_name");
metaDataService.getColumn(authorizationMatrix, "role_id", "role_id", "TEXT", null, 6, Boolean.TRUE, "Role", Boolean.FALSE, null, "name");
metaDataService.getColumn(tokenTable, "profile_id", "profile_id", "TEXT", null, 9, Boolean.TRUE, "Profile", Boolean.FALSE, null, "user_name");
MetaDataTable permissionTable = metaDataService.getTable("permission");
metaDataService.getColumn(permissionTable, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(permissionTable, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(permissionTable, "last_modified_date", "modified", "TIMESTAMP", null, 3, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(permissionTable, "version", "version", "LONG", null, 4, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(permissionTable, "name", "name", "TEXT", null, 5, Boolean.TRUE, "", Boolean.FALSE, null);
MetaDataTable AssignmentTable = metaDataService.getTable("assignment");
metaDataService.getColumn(AssignmentTable, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(AssignmentTable, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(AssignmentTable, "last_modified_date", "modified", "TIMESTAMP", null, 3, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(AssignmentTable, "version", "version", "LONG", null, 4, Boolean.FALSE, "", Boolean.FALSE, null);
metaDataService.getColumn(AssignmentTable, "profile_id", "profile_id", "TEXT", null, 5, Boolean.TRUE, "Profile", Boolean.FALSE, null, "user_name");
metaDataService.getColumn(AssignmentTable, "permission_id", "permission_id", "TEXT", null, 6, Boolean.TRUE, "Permission", Boolean.FALSE, null, "name");
MetaDataTable moduleDataTable = metaDataService.getTable("module_data");
metaDataService.getColumn(moduleDataTable, "id", "identifier", "TEXT", "PRIMARY KEY", 1, Boolean.TRUE, "", Boolean.FALSE, null);
metaDataService.getColumn(moduleDataTable, "created_date", "created", "TIMESTAMP", null, 2, Boolean.FALSE, "", Boolean.FALSE, null);
@@ -0,0 +1,34 @@
package de.thpeetz.kontor.admin.data;
import de.thpeetz.kontor.common.data.AbstractEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Assignment extends AbstractEntity {
@ManyToOne
@JoinColumn(name = "profile_id")
@NotNull
private Profile profile;
@ManyToOne
@JoinColumn(name = "permission_id")
@NotNull
private Permission permission;
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Assignment{");
sb.append("profile=").append(profile.getUserName());
sb.append(", permission=").append(permission.getName());
sb.append('}');
return sb.toString();
}
}
@@ -0,0 +1,29 @@
package de.thpeetz.kontor.admin.data;
import de.thpeetz.kontor.common.data.AbstractEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import java.util.List;
@Slf4j
@Getter
@Setter
@ToString
@Entity
public class Permission extends AbstractEntity {
@NotEmpty
private String name;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "permission")
@Nullable
private List<Assignment> assignments;
}
@@ -0,0 +1,57 @@
package de.thpeetz.kontor.admin.data;
import de.thpeetz.kontor.common.data.AbstractEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import java.util.LinkedList;
import java.util.List;
@Slf4j
@Getter
@Setter
@ToString
@Entity
@Table(indexes = @Index(columnList = "userName"), uniqueConstraints = @UniqueConstraint(columnNames = {"userName"}))
public class Profile extends AbstractEntity {
private String firstName;
private String lastName;
@NotEmpty
private String userName;
private String email;
private String password;
private boolean enabled;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "profile")
@Nullable
private List<Assignment> assignments = new LinkedList<>();
@OneToMany(fetch = FetchType.EAGER, mappedBy = "profile")
@Nullable
private List<Token> tokens = new LinkedList<>();
public String getFullName() {
StringBuilder fullNameBuilder = new StringBuilder();
if (firstName != null) {
fullNameBuilder.append(firstName);
}
if (lastName != null) {
if (!fullNameBuilder.isEmpty()) {
fullNameBuilder.append(" ");
}
fullNameBuilder.append(lastName);
}
return fullNameBuilder.toString();
}
}
@@ -31,7 +31,7 @@ public class Token extends AbstractEntity {
private boolean enabled;
@ManyToOne
@JoinColumn(name="user_id")
@JoinColumn(name="profile_id")
@NotNull
private User user;
private Profile profile;
}
@@ -0,0 +1,13 @@
package de.thpeetz.kontor.admin.repository;
import de.thpeetz.kontor.admin.data.*;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface AssignmentRepository extends JpaRepository<Assignment, String> {
List<Assignment> findByProfile(Profile profile);
List<Assignment> findByPermission(Permission permission);
}
@@ -0,0 +1,19 @@
package de.thpeetz.kontor.admin.repository;
import de.thpeetz.kontor.admin.data.Permission;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface PermissionRepository extends JpaRepository<Permission, String> {
@Query("select p from Permission p " +
"where lower(p.name) like lower(concat('%', :searchTerm, '%')) ")
List<Permission> search(@Param("searchTerm") String searchTerm);
@Query("select p from Permission p " +
"where lower(p.name) like lower(:name) ")
Permission findByName(@Param("name") String name);
}
@@ -0,0 +1,18 @@
package de.thpeetz.kontor.admin.repository;
import de.thpeetz.kontor.admin.data.Profile;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.UUID;
public interface ProfileRepository extends JpaRepository<Profile, String> {
@Query("select p from Profile p " +
"where lower(p.lastName) like lower(concat('%', :searchTerm, '%')) ")
List<Profile> search(@Param("searchTerm") String searchTerm);
Profile findByUserName(String userName);
}
@@ -3,108 +3,105 @@ package de.thpeetz.kontor.admin.services;
import java.util.Collection;
import java.util.List;
import de.thpeetz.kontor.admin.data.*;
import de.thpeetz.kontor.admin.repository.*;
import org.springframework.stereotype.Service;
import de.thpeetz.kontor.admin.data.AuthorizationMatrix;
import de.thpeetz.kontor.admin.repository.AuthorizationMatrixRepository;
import de.thpeetz.kontor.admin.data.Role;
import de.thpeetz.kontor.admin.repository.RoleRepository;
import de.thpeetz.kontor.admin.data.User;
import de.thpeetz.kontor.admin.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class AdminService {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final AuthorizationMatrixRepository authorizationMatrixRepository;
public AdminService(UserRepository userRepository, RoleRepository roleRepository,
AuthorizationMatrixRepository authorizationMatrixRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.authorizationMatrixRepository = authorizationMatrixRepository;
private final ProfileRepository profileRepository;
private final PermissionRepository permissionRepository;
private final AssignmentRepository assignmentRepository;
public AdminService(ProfileRepository profileRepository,
PermissionRepository permissionRepository,
AssignmentRepository assignmentRepository) {
this.profileRepository = profileRepository;
this.permissionRepository = permissionRepository;
this.assignmentRepository = assignmentRepository;
}
public List<User> findAllUsers() {
return userRepository.findAll();
public List<Profile> findAllProfiles() { return profileRepository.findAll(); }
public Profile getProfile(String userName) {
log.debug("get Profile {}", userName);
return profileRepository.findByUserName(userName);
}
public List<Role> findAllRoles() {
return roleRepository.findAll();
}
public List<Permission> findAllPermissions() { return permissionRepository.findAll(); }
public Collection<Role> findAllRoles(String stringFilter) {
public Collection<Permission> findAllPermissions(String stringFilter) {
if (stringFilter == null || stringFilter.isEmpty()) {
return roleRepository.findAll();
return permissionRepository.findAll();
} else {
return roleRepository.search(stringFilter);
return permissionRepository.search(stringFilter);
}
}
public Role addRole(String roleName) {
Role role = roleRepository.findByName(roleName);
if (role == null) {
log.info("Role {} was not found, will create it.", roleName);
role = new Role();
role.setName(roleName);
roleRepository.save(role);
public Permission addPermission(String permissionName) {
Permission permission = permissionRepository.findByName(permissionName);
if (permission == null) {
log.info("Permission {} was not found, will create it.", permissionName);
permission = new Permission();
permission.setName(permissionName);
permissionRepository.save(permission);
}
return role;
return permission;
}
public void saveRole(Role role) {
if (role == null) {
log.warn("Role is null. Can't save it.");
public void savePermission(Permission permission) {
if (permission == null) {
log.warn("Permission is null. Can't save it.");
}
log.info("saveRole: role={}", role);
roleRepository.save(role);
log.info("savePermission: permission={}", permission);
permissionRepository.save(permission);
}
public void deleteRole(Role role) {
if (role == null) {
log.warn("Role is null. Can't delete it.");
public void deletePermission(Permission permission) {
if (permission == null) {
log.warn("Permission is null. Can't delete it.");
return;
}
log.info("deleteRole: role={}", role);
roleRepository.delete(role);
log.info("deletePermission: permission={}", permission);
permissionRepository.delete(permission);
}
public List<AuthorizationMatrix> findAllAuthorizationMatrices() {
return authorizationMatrixRepository.findAll();
public List<Assignment> findAllAssignments() {
return assignmentRepository.findAll();
}
public void saveAuthorizationMatrix(AuthorizationMatrix authorizationMatrix) {
if (authorizationMatrix == null) {
log.warn("AuthorizationMatrix is null. Can't save it.");
public void saveAssignment(Assignment assignment) {
if (assignment == null) {
log.warn("Assignment is null. Can't save it.");
return;
}
log.info("saveAuthorizationMatrix: authorizationMatrix={}", authorizationMatrix);
authorizationMatrixRepository.save(authorizationMatrix);
log.info("saveAssignment: assignment={}", assignment);
assignmentRepository.save(assignment);
}
public void deleteAuthorizationMatrix(AuthorizationMatrix authorizationMatrix) {
if (authorizationMatrix == null) {
log.warn("AuthorizationMatrix is null. Can't delete it.");
public void deleteAssignment(Assignment assignment) {
if (assignment == null) {
log.warn("Assignment is null. Can't delete it.");
return;
}
log.info("deleteAuthorizationMatrix: authorizationMatrix={}", authorizationMatrix);
authorizationMatrixRepository.delete(authorizationMatrix);
log.info("deleteAssignment: assignment={}", assignment);
assignmentRepository.delete(assignment);
}
public String getUserFullName(String userName) {
log.debug("get Fullname für user {}", userName);
User user = userRepository.findByUserName(userName);
if (user == null) {
public String getProfileFullName(String userName) {
log.debug("get Fullname für Profile {}", userName);
Profile profile = profileRepository.findByUserName(userName);
if (profile == null) {
log.info("keinen Eintrag für {} gefunden", userName);
return userName;
} else {
log.info("Voller Name des User {}: {}", userName, user.getFullName());
return user.getFullName();
log.info("Voller Name des Profile {}: {}", userName, profile.getFullName());
return profile.getFullName();
}
}
public User getUser(String userName) {
log.debug("get User {}", userName);
return userRepository.findByUserName(userName);
}
}
@@ -1,9 +1,7 @@
package de.thpeetz.kontor.admin.services;
import de.thpeetz.kontor.admin.data.*;
import de.thpeetz.kontor.admin.repository.AuthorizationMatrixRepository;
import de.thpeetz.kontor.admin.repository.RoleRepository;
import de.thpeetz.kontor.admin.repository.UserRepository;
import de.thpeetz.kontor.admin.repository.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
@@ -26,83 +24,86 @@ public class KontorUserDetailsService implements UserDetailsService {
private static SecureRandom random = new SecureRandom();
@Autowired
private UserRepository userRepository;
private ProfileRepository profileRepository;
@Autowired
private RoleRepository roleRepository;
private PermissionRepository permissionRepository;
@Autowired
private AuthorizationMatrixRepository authorizationMatrixRepository;
private AssignmentRepository assignmentRepository;
public Collection<User> findAllUsers(String stringFilter) {
public Collection<Profile> findAllProfiles(String stringFilter) {
if (stringFilter == null || stringFilter.isEmpty()) {
return userRepository.findAll();
return profileRepository.findAll();
} else {
return userRepository.search(stringFilter);
return profileRepository.search(stringFilter);
}
}
public void saveUser(User user) {
if (user == null) {
log.warn("User is null. Can't save it.");
public void saveProfile(Profile profile) {
if (profile == null) {
log.warn("Profile is null. Can't save it.");
return;
}
log.info("saveUser: user={}", user);
userRepository.save(user);
log.info("saveProfile: profile={}", profile);
profileRepository.save(profile);
}
public void saveUser(User user, List<Role> roles) {
if (user == null) {
log.warn("User is null. Can't save it.");
public void saveProfile(Profile profile, List<Permission> permissions) {
if (profile == null) {
log.warn("Profile is null. Can't save it.");
return;
}
log.info("First save user: {}", user);
user = userRepository.save(user);
List<Role> copy = roles.stream().collect(Collectors.toList());
List<AuthorizationMatrix> permissions = user.getMatrix();
permissions.forEach(matrix -> {
if (roles.contains(matrix.getRole())) {
log.info("Role {} already assigned", matrix.getRole());
copy.remove(matrix.getRole());
log.info("First save Profile: {}", profile);
profile = profileRepository.save(profile);
List<Permission> copy = permissions.stream().collect(Collectors.toList());
List<Assignment> assignments = profile.getAssignments();
assignments.forEach(assignment -> {
if (permissions.contains(assignment.getPermission())) {
log.info("Permission {} already assigned", assignment.getPermission());
copy.remove(assignment.getPermission());
} else {
log.info("Role {} has to be removed", matrix.getRole());
authorizationMatrixRepository.delete(matrix);
log.info("Permission {} has to be removed", assignment.getPermission());
assignmentRepository.delete(assignment);
}
});
log.info("remaining roles: {}", copy);
for (Role role : copy) {
AuthorizationMatrix matrix = new AuthorizationMatrix();
matrix.setUser(user);
matrix.setRole(role);
authorizationMatrixRepository.save(matrix);
for (Permission permission : copy) {
Assignment assignment = new Assignment();
assignment.setProfile(profile);
assignment.setPermission(permission);
assignmentRepository.save(assignment);
}
}
public void deleteUser(User user) {
if (user == null) {
log.warn("User is null. Can't delete it.");
public void deleteProfile(Profile profile) {
if (profile == null) {
log.warn("Profile is null. Can't delete it.");
return;
}
log.info("deleteUser: user={}", user);
userRepository.delete(user);
log.info("deleteProfile: profile={}", profile);
profileRepository.delete(profile);
}
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
log.info("loadUserByUsername: userName={}", userName);
User user = userRepository.findByUserName(userName);
if (user == null) {
Profile profile = profileRepository.findByUserName(userName);
if (profile == null) {
log.info("User not found");
return null;
}
Collection<? extends GrantedAuthority> authorities = getAuthorities(user);
log.info("User {} hat Rolen: {}", userName, authorities);
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(),
Collection<? extends GrantedAuthority> authorities = getAuthorities(profile);
log.info("Profile {} hat Permissions: {}", userName, authorities);
return new org.springframework.security.core.userdetails.User(profile.getUserName(), profile.getPassword(),
authorities);
}
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
return authorizationMatrixRepository.findByUser(user).stream()
.map(matrix -> matrix.getRole().getName())
private Collection<? extends GrantedAuthority> getAuthorities(Profile profile) {
return assignmentRepository.findByProfile(profile).stream()
.map(assignment -> assignment.getPermission().getName())
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@@ -122,7 +123,7 @@ public class KontorUserDetailsService implements UserDetailsService {
log.info("removeRememberedUser: id={}", id);
}
public List<Role> findAllRoles() {
return roleRepository.findAll();
public List<Permission> findAllPermissions() {
return permissionRepository.findAll();
}
}
@@ -29,7 +29,7 @@ public class AdminLayout extends AppLayout {
private HorizontalLayout getSecondaryNavigation() {
HorizontalLayout navigation = new HorizontalLayout();
navigation.addClassNames(LumoUtility.JustifyContent.CENTER, LumoUtility.Gap.SMALL, LumoUtility.Height.MEDIUM);
navigation.add(AdminConstants.getUserNavigation(), AdminConstants.getRoleNavigation(),
navigation.add(AdminConstants.getProfileNavigation(), AdminConstants.getPermissionNavigation(),
AdminConstants.getAuthorizationNavigation());
return navigation;
}
@@ -0,0 +1,112 @@
package de.thpeetz.kontor.admin.views;
import java.util.List;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import de.thpeetz.kontor.admin.data.Assignment;
import de.thpeetz.kontor.admin.data.Permission;
import de.thpeetz.kontor.admin.data.Profile;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AssignmentForm extends FormLayout {
ComboBox<Profile> profile = new ComboBox<>("Proile");
ComboBox<Permission> permission = new ComboBox<>("Permission");
Button save = new Button("Save");
Button delete = new Button("Delete");
Button close = new Button("Cancel");
Binder<Assignment> binder = new BeanValidationBinder<>(Assignment.class);
public AssignmentForm(List<Profile> profiles, List<Permission> permissions) {
addClassName("assignment-form");
binder.bindInstanceFields(this);
profile.setItems(profiles);
profile.setItemLabelGenerator(Profile::getUserName);
permission.setItems(permissions);
permission.setItemLabelGenerator(Permission::getName);
add(profile, permission, createButtonsLayout());
}
private HorizontalLayout createButtonsLayout() {
save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
save.addClickShortcut(Key.ENTER);
close.addClickShortcut(Key.ESCAPE);
save.addClickListener(event -> validateAndSave());
delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean())));
close.addClickListener(event -> fireEvent(new CloseEvent(this)));
binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
return new HorizontalLayout(save, delete, close);
}
private void validateAndSave() {
if (binder.isValid()) {
fireEvent(new SaveEvent(this, binder.getBean()));
}
}
public void setAssignment(Assignment assignment) {
binder.setBean(assignment);
}
public abstract static class AssignmentFormEvent extends ComponentEvent<AssignmentForm> {
private Assignment assignment;
protected AssignmentFormEvent(AssignmentForm source, Assignment assignment) {
super(source, false);
this.assignment = assignment;
}
public Assignment getAssignment() {
return assignment;
}
}
public static class SaveEvent extends AssignmentFormEvent {
SaveEvent(AssignmentForm source, Assignment assignment) {
super(source, assignment);
}
}
public static class DeleteEvent extends AssignmentFormEvent {
DeleteEvent(AssignmentForm source, Assignment assignment) {
super(source, assignment);
}
}
public static class CloseEvent extends AssignmentFormEvent {
CloseEvent(AssignmentForm source) {
super(source, null);
}
}
public void addDeleteListener(ComponentEventListener<DeleteEvent> listener) {
addListener(DeleteEvent.class, listener);
}
public void addSaveListener(ComponentEventListener<SaveEvent> listener) {
addListener(SaveEvent.class, listener);
}
public void addCloseListener(ComponentEventListener<CloseEvent> listener) {
addListener(CloseEvent.class, listener);
}
}
@@ -0,0 +1,114 @@
package de.thpeetz.kontor.admin.views;
import de.thpeetz.kontor.admin.data.Assignment;
import org.springframework.context.annotation.Scope;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import de.thpeetz.kontor.admin.AdminConstants;
import de.thpeetz.kontor.admin.services.AdminService;
import de.thpeetz.kontor.common.views.MainLayout;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SpringComponent
@Scope("prototype")
@RolesAllowed("ROLE_ADMIN")
@Route(value = AdminConstants.ASSIGNMENT_ROUTE, layout = MainLayout.class)
@PageTitle("Authorization | Admin | Kontor")
public class AssignmentView extends VerticalLayout {
Grid<Assignment> grid = new Grid<>(Assignment.class);
AssignmentForm form;
AdminService service;
public AssignmentView(AdminService service) {
this.service = service;
addClassName("assignment-view");
setSizeFull();
configureGrid();
configureForm();
add(getToolbar(), getContent());
updateList();
}
private void configureGrid() {
grid.addClassName("assignment-grid");
grid.setSizeFull();
grid.setColumns("profile.userName", "permission.name");
grid.getColumns().forEach(col -> col.setAutoWidth(true));
grid.asSingleSelect().addValueChangeListener(event -> editAssignment(event.getValue()));
}
private void configureForm() {
form = new AssignmentForm(service.findAllProfiles(), service.findAllPermissions());
form.setWidth("25em");
form.addSaveListener(this::saveAssignment);
form.addDeleteListener(this::deleteAssignment);
form.addCloseListener(e -> closeEditor());
}
private void saveAssignment(AssignmentForm.SaveEvent event) {
Assignment assignment = event.getAssignment();
service.saveAssignment(assignment);
updateList();
closeEditor();
}
private void deleteAssignment(AssignmentForm.DeleteEvent event) {
service.deleteAssignment(event.getAssignment());
updateList();
closeEditor();
}
private Component getContent() {
HorizontalLayout content = new HorizontalLayout(grid, form);
content.setFlexGrow(2, grid);
content.setFlexGrow(1, form);
content.addClassName("content");
content.setSizeFull();
return content;
}
private HorizontalLayout getToolbar() {
Button addAuthorizationMaxtrixButton = new Button("Add permission", click -> addAssignment());
HorizontalLayout toolbar = new HorizontalLayout(addAuthorizationMaxtrixButton);
toolbar.addClassName("toolbar");
return toolbar;
}
public void editAssignment(Assignment assignment) {
if (assignment == null) {
closeEditor();
} else {
form.setAssignment(assignment);
form.setVisible(true);
addClassName("editing");
}
}
public void closeEditor() {
form.setAssignment(null);
form.setVisible(false);
removeClassName("editing");
}
private void addAssignment() {
grid.asSingleSelect().clear();
editAssignment(new Assignment());
}
private void updateList() {
grid.setItems(service.findAllAssignments());
}
}
@@ -0,0 +1,101 @@
package de.thpeetz.kontor.admin.views;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import de.thpeetz.kontor.admin.data.Permission;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class PermissionForm extends FormLayout {
TextField name = new TextField("Role name");
Button save = new Button("Save");
Button delete = new Button("Delete");
Button close = new Button("Cancel");
Binder<Permission> binder = new BeanValidationBinder<>(Permission.class);
public PermissionForm() {
addClassName("permission-form");
binder.bindInstanceFields(this);
add(name, createButtonsLayout());
}
private HorizontalLayout createButtonsLayout() {
save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
save.addClickShortcut(Key.ENTER);
close.addClickShortcut(Key.ESCAPE);
save.addClickListener(event -> validateAndSave());
delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean())));
close.addClickListener(event -> fireEvent(new CloseEvent(this)));
binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
return new HorizontalLayout(save, delete, close);
}
private void validateAndSave() {
if (binder.isValid()) {
fireEvent(new SaveEvent(this, binder.getBean()));
}
}
public void setPermission(Permission permission) {
binder.setBean(permission);
}
public abstract static class PermissionFormEvent extends ComponentEvent<PermissionForm> {
private Permission permission;
protected PermissionFormEvent(PermissionForm source, Permission permission) {
super(source, false);
this.permission = permission;
}
public Permission getPermission() {
return permission;
}
}
public static class SaveEvent extends PermissionFormEvent {
SaveEvent(PermissionForm source, Permission permission) {
super(source, permission);
}
}
public static class DeleteEvent extends PermissionFormEvent {
DeleteEvent(PermissionForm source, Permission permission) {
super(source, permission);
}
}
public static class CloseEvent extends PermissionFormEvent {
CloseEvent(PermissionForm source) {
super(source, null);
}
}
public void addDeleteListener(ComponentEventListener<DeleteEvent> listener) {
addListener(DeleteEvent.class, listener);
}
public void addSaveListener(ComponentEventListener<SaveEvent> listener) {
addListener(SaveEvent.class, listener);
}
public void addCloseListener(ComponentEventListener<CloseEvent> listener) {
addListener(CloseEvent.class, listener);
}
}
@@ -0,0 +1,119 @@
package de.thpeetz.kontor.admin.views;
import de.thpeetz.kontor.admin.data.Permission;
import org.springframework.context.annotation.Scope;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import de.thpeetz.kontor.admin.AdminConstants;
import de.thpeetz.kontor.admin.services.AdminService;
import de.thpeetz.kontor.common.views.MainLayout;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SpringComponent
@Scope("prototype")
@RolesAllowed("ROLE_ADMIN")
@Route(value = AdminConstants.PERMISSION_ROUTE, layout = MainLayout.class)
@PageTitle("Permissions | Admin | Kontor")
public class PermissionView extends VerticalLayout {
Grid<Permission> grid = new Grid<>(Permission.class);
TextField filterText = new TextField();
PermissionForm form;
AdminService service;
public PermissionView(AdminService service) {
this.service = service;
addClassName("permission-view");
setSizeFull();
configureGrid();
configureForm();
add(getToolbar(), getContent());
updateList();
}
private void configureGrid() {
grid.addClassName("permission-grid");
grid.setSizeFull();
grid.setColumns("name");
grid.getColumns().forEach(col -> col.setAutoWidth(true));
grid.asSingleSelect().addValueChangeListener(event -> editPermission(event.getValue()));
}
private void configureForm() {
form = new PermissionForm();
form.setWidth("25em");
form.addSaveListener(this::savePermission);
form.addDeleteListener(this::deletePermission);
form.addCloseListener(e -> closeEditor());
}
private void savePermission(PermissionForm.SaveEvent event) {
service.savePermission(event.getPermission());
updateList();
closeEditor();
}
private void deletePermission(PermissionForm.DeleteEvent event) {
service.deletePermission(event.getPermission());
updateList();
closeEditor();
}
private Component getContent() {
HorizontalLayout content = new HorizontalLayout(grid, form);
content.setFlexGrow(2, grid);
content.setFlexGrow(1, form);
content.addClassName("content");
content.setSizeFull();
return content;
}
private HorizontalLayout getToolbar() {
filterText.setPlaceholder("Filter by user name...");
filterText.setClearButtonVisible(true);
filterText.setValueChangeMode(ValueChangeMode.LAZY);
filterText.addValueChangeListener(e -> updateList());
Button addUserButton = new Button("Add user", click -> addPermission());
HorizontalLayout toolbar = new HorizontalLayout(filterText, addUserButton);
toolbar.addClassName("toolbar");
return toolbar;
}
public void editPermission(Permission permission) {
if (permission == null) {
closeEditor();
} else {
form.setPermission(permission);
form.setVisible(true);
addClassName("editing");
}
}
public void closeEditor() {
form.setPermission(null);
form.setVisible(false);
removeClassName("editing");
}
private void addPermission() {
grid.asSingleSelect().clear();
editPermission(new Permission());
}
private void updateList() {
grid.setItems(service.findAllPermissions(filterText.getValue()));
}
}
@@ -0,0 +1,143 @@
package de.thpeetz.kontor.admin.views;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.checkbox.CheckboxGroup;
import com.vaadin.flow.component.checkbox.CheckboxGroupVariant;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import de.thpeetz.kontor.admin.data.Permission;
import de.thpeetz.kontor.admin.data.Profile;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
@Slf4j
public class ProfileForm extends FormLayout {
TextField userName = new TextField("User name");
PasswordField password = new PasswordField("Password");
EmailField email = new EmailField("Email");
TextField firstName = new TextField("First name");
TextField lastName = new TextField("Last name");
Checkbox enabled = new Checkbox("Enabled");
String originalPassword;
CheckboxGroup<Permission> permissionList = new CheckboxGroup<>("Permissions");
Button save = new Button("Save");
Button delete = new Button("Delete");
Button close = new Button("Cancel");
Binder<Profile> binder = new BeanValidationBinder<>(Profile.class);
public ProfileForm() {
addClassName("profile-form");
binder.bindInstanceFields(this);
add(userName, password, email, firstName, lastName, enabled, configurePermissionsGroup(), createButtonsLayout());
}
private CheckboxGroup<Permission> configurePermissionsGroup() {
permissionList.addThemeVariants(CheckboxGroupVariant.LUMO_VERTICAL);
permissionList.setItemLabelGenerator(Permission::getName);
permissionList.addValueChangeListener(event -> {
log.debug("permissions changed: {}", event);
});
return permissionList;
}
private HorizontalLayout createButtonsLayout() {
save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
save.addClickShortcut(Key.ENTER);
close.addClickShortcut(Key.ESCAPE);
save.addClickListener(event -> validateAndSave());
delete.addClickListener(event -> fireEvent(new ProfileForm.DeleteEvent(this, binder.getBean())));
close.addClickListener(event -> fireEvent(new ProfileForm.CloseEvent(this)));
binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
return new HorizontalLayout(save, delete, close);
}
private void validateAndSave() {
if (binder.isValid()) {
fireEvent(new ProfileForm.SaveEvent(this, binder.getBean()));
}
}
public void setProfile(Profile profile) {
binder.setBean(profile);
//log.debug("UserForm.setUser: {}", user);
if (profile != null) {
this.originalPassword = profile.getPassword();
} else {
this.originalPassword = null;
}
}
public void setPermissions(List<Permission> permissions, Profile profile) {
permissionList.setItems(permissions);
profile.getAssignments().stream().forEach(assignment -> {
permissionList.select(assignment.getPermission());
});
}
public boolean hasPasswordChanged(Profile profile) {
return !originalPassword.equals(profile.getPassword());
}
public abstract static class ProfileFormEvent extends ComponentEvent<ProfileForm> {
private Profile profile;
protected ProfileFormEvent(ProfileForm source, Profile profile) {
super(source, false);
this.profile = profile;
}
public Profile getProfile() {
return profile;
}
}
public static class SaveEvent extends ProfileForm.ProfileFormEvent {
SaveEvent(ProfileForm source, Profile profile) {
super(source, profile);
}
}
public static class DeleteEvent extends ProfileForm.ProfileFormEvent {
DeleteEvent(ProfileForm source, Profile profile) {
super(source, profile);
}
}
public static class CloseEvent extends ProfileForm.ProfileFormEvent {
CloseEvent(ProfileForm source) {
super(source, null);
}
}
public void addDeleteListener(ComponentEventListener<ProfileForm.DeleteEvent> listener) {
addListener(ProfileForm.DeleteEvent.class, listener);
}
public void addSaveListener(ComponentEventListener<ProfileForm.SaveEvent> listener) {
addListener(ProfileForm.SaveEvent.class, listener);
}
public void addCloseListener(ComponentEventListener<ProfileForm.CloseEvent> listener) {
addListener(ProfileForm.CloseEvent.class, listener);
}
}
@@ -0,0 +1,135 @@
package de.thpeetz.kontor.admin.views;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.SpringComponent;
import de.thpeetz.kontor.admin.data.Permission;
import de.thpeetz.kontor.admin.data.Profile;
import de.thpeetz.kontor.admin.services.KontorUserDetailsService;
import de.thpeetz.kontor.common.views.MainLayout;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@SpringComponent
@Scope("prototype")
@RolesAllowed("ROLE_ADMIN")
@Route(value = "admin/profile", layout = MainLayout.class)
@PageTitle("Profile | Admin | Kontor")
public class ProfileView extends VerticalLayout {
Grid<Profile> grid = new Grid<>(Profile.class);
TextField filterText = new TextField();
ProfileForm form;
KontorUserDetailsService service;
@Autowired
PasswordEncoder passwordEncoder;
public ProfileView(KontorUserDetailsService service) {
this.service = service;
addClassName("profile-view");
setSizeFull();
configureGrid();
configureForm();
add(getToolbar(), getContent());
updateList();
}
private void configureGrid() {
grid.addClassName("profile-grid");
grid.setSizeFull();
grid.setColumns("userName", "email", "firstName", "lastName", "enabled");
grid.getColumns().forEach(col -> col.setAutoWidth(true));
grid.asSingleSelect().addValueChangeListener(event -> editProfile(event.getValue()));
}
private void configureForm() {
form = new ProfileForm();
form.setWidth("25em");
form.setVisible(false);
form.addSaveListener(this::saveProfile);
form.addDeleteListener(this::deleteProfile);
form.addCloseListener(e -> closeEditor());
}
private void saveProfile(ProfileForm.SaveEvent event) {
Profile profile = event.getProfile();
log.debug("ProfileView.saveProfile: {}", profile);
List<Permission> permissions = form.permissionList.getSelectedItems().stream().collect(Collectors.toList());
log.info("selected permissions: {}", permissions);
if (form.hasPasswordChanged(profile)) {
profile.setPassword(passwordEncoder.encode(profile.getPassword()));
log.debug("password changed for profile {}", profile);
}
service.saveProfile(profile, permissions);
updateList();
closeEditor();
}
private void deleteProfile(ProfileForm.DeleteEvent event) {
service.deleteProfile(event.getProfile());
updateList();
closeEditor();
}
private Component getContent() {
HorizontalLayout content = new HorizontalLayout(grid, form);
content.setFlexGrow(2, grid);
content.setFlexGrow(1, form);
content.addClassName("content");
content.setSizeFull();
return content;
}
private HorizontalLayout getToolbar() {
filterText.setPlaceholder("Filter by user name...");
filterText.setClearButtonVisible(true);
filterText.setValueChangeMode(ValueChangeMode.LAZY);
filterText.addValueChangeListener(e -> updateList());
Button addProfileButton = new Button("Add profile", click -> addProfile());
HorizontalLayout toolbar = new HorizontalLayout(filterText, addProfileButton);
toolbar.addClassName("toolbar");
return toolbar;
}
public void editProfile(Profile profile) {
if (profile == null) {
closeEditor();
} else {
form.setProfile(profile);
form.setPermissions(service.findAllPermissions(), profile);
form.setVisible(true);
addClassName("editing");
}
}
public void closeEditor() {
form.setProfile(null);
form.setVisible(false);
removeClassName("editing");
}
private void addProfile() {
grid.asSingleSelect().clear();
editProfile(new Profile());
}
private void updateList() {
grid.setItems(service.findAllProfiles(filterText.getValue()));
}
}
@@ -10,7 +10,7 @@ import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.thpeetz.kontor.admin.data.User;
import de.thpeetz.kontor.admin.data.Profile;
import de.thpeetz.kontor.admin.services.AdminService;
import de.thpeetz.kontor.common.views.MainLayout;
import de.thpeetz.kontor.security.SecurityService;
@@ -32,7 +32,7 @@ public class UserProfileView extends VerticalLayout {
Button save = new Button("Save");
Button close = new Button("Cancel");
Binder<User> binder = new BeanValidationBinder<>(User.class);
Binder<Profile> binder = new BeanValidationBinder<>(Profile.class);
public UserProfileView(AdminService adminService, SecurityService securityService) {
this.adminService = adminService;
@@ -42,7 +42,7 @@ public class UserProfileView extends VerticalLayout {
add(firstName, lastName, createButtonsLayout());
securityService.getAuthenticatedUser().ifPresent(user -> {
log.info("UserProfileView: {}", user.getUsername());
binder.setBean(adminService.getUser(user.getUsername()));
binder.setBean(adminService.getProfile(user.getUsername()));
});
}
@@ -32,7 +32,7 @@ public class AvatarMenuBar extends Div {
log.info("AdminService: {}", adminService);
if (user != null) {
String userName = user.getUsername();
String fullName = adminService.getUserFullName(userName);
String fullName = adminService.getProfileFullName(userName);
avatar.setName(fullName);
}
});
@@ -23,7 +23,7 @@ public class SetupModuleMedia implements ApplicationListener<ContextRefreshedEve
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
adminService.addRole(MediaConstants.MEDIA_ROLE);
adminService.addPermission(MediaConstants.MEDIA_ROLE);
if (alreadySetup) {
log.info("SetupModuleMedia already executed, skipping");
return;