From e4b957dfe9f1bcf553d22a70fff3bb4c67911124 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:41:56 +0100 Subject: [PATCH 01/13] --wip-- [skip ci] --- build.gradle | 3 +- .../project/ContainerController.java | 15 +-- .../project/GroupController.java | 17 +--- .../project/MyUserDetailsService.java | 83 ++++++++++++++++ .../project/Security.java | 25 ----- .../project/SetupDataLoader.java | 93 ++++++++++++++++++ .../StockManagementSystemApplication.java | 19 ++++ .../data/authentication/Privilege.java | 48 +++++++++ .../project/data/authentication/Role.java | 64 ++++++++++++ .../project/data/authentication/User.java | 97 +++++++++++++++++++ .../authentication/PrivilegeRepository.java | 11 +++ .../authentication/RoleRepository.java | 12 +++ .../authentication/UserRepository.java | 13 +++ src/main/resources/application.yml | 3 + 14 files changed, 447 insertions(+), 56 deletions(-) create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java delete mode 100644 src/main/java/com/sms/stockmanagementsystem/project/Security.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java diff --git a/build.gradle b/build.gradle index e3d1f66..afcd863 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,8 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' compileOnly 'org.projectlombok:lombok' runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.projectlombok:lombok' @@ -32,7 +34,6 @@ dependencies { implementation 'com.googlecode.json-simple:json-simple:1.1.1' implementation 'org.jetbrains:annotations:15.0' implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.3.1' - implementation 'org.hsqldb:hsqldb:2.7.1' implementation 'me.paulschwarz:spring-dotenv:4.0.0' } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java b/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java index 772cb0e..d0fed33 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java @@ -20,8 +20,6 @@ @RequestMapping("/sms/api") public class ContainerController { - private final Security security; - private final ContainerRepository containerRepository; private final GroupRepository groupRepository; @@ -35,10 +33,8 @@ public String health() { @PostMapping("/container") public Container setContainerDetails( @RequestBody String data, - @RequestParam("secret") @NotNull String secret, @NotNull @RequestParam("server") String serverName) throws ParseException { - security.checkSecret(secret); JSONParser parser = new JSONParser(); JSONObject jsonObject = (JSONObject) parser.parse(data); @@ -65,10 +61,8 @@ public Container setContainerDetails( @CrossOrigin @GetMapping("/container") public Container getContainerDetails( - @NotNull @RequestParam("secret") String secret, @NotNull @RequestParam("containerId") String name, @NotNull @RequestParam("server") String server) { - security.checkSecret(secret); List item = containerRepository.findByNameAndServer(name, server); if (item.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST); @@ -79,10 +73,8 @@ public Container getContainerDetails( @CrossOrigin @DeleteMapping("/container") public String deleteContainerItem( - @NotNull @RequestParam("secret") String secret, @NotNull @RequestParam("containerId") String name, @NotNull @RequestParam("server") String server) { - security.checkSecret(secret); List containerList = containerRepository.findByNameAndServer(name, server); if (containerList.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); @@ -99,9 +91,7 @@ public String deleteContainerItem( @CrossOrigin @GetMapping("/getContainers") public List getAllContainers( - @NotNull @RequestParam("secret") String secret, @RequestParam(value = "server", required = false) String server) { - security.checkSecret(secret); return server != null ? containerRepository.findByServer(server) : containerRepository.findAll(); @@ -109,10 +99,8 @@ public List getAllContainers( @GetMapping("/getContainerGroups") public List getContainerGroups( - @RequestParam("secret") String secret, @RequestParam("server") String server, @RequestParam("containerId") String name) { - security.checkSecret(secret); List containerList = containerRepository.findByNameAndServer(name, server); if (containerList.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); @@ -122,8 +110,7 @@ public List getContainerGroups( } @Autowired - public ContainerController(Security security, ContainerRepository containerRepository, GroupRepository groupRepository) { - this.security = security; + public ContainerController(ContainerRepository containerRepository, GroupRepository groupRepository) { this.containerRepository = containerRepository; this.groupRepository = groupRepository; } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java b/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java index 1c959a3..9b3d3e7 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java @@ -18,7 +18,6 @@ @RequestMapping("/sms/api") public class GroupController { - private final Security security; private final GroupRepository groupRepository; @@ -26,10 +25,8 @@ public class GroupController { @PostMapping("/createGroup") public String createGroup( - @NotNull @RequestParam("secret") String secret, @NotNull @RequestParam("name") String name, @RequestParam("user") String user) { - security.checkSecret(secret); if (!groupRepository.findByName(name).isEmpty()) { throw new ResponseStatusException(HttpStatus.CONFLICT, "group already exists"); } @@ -40,11 +37,9 @@ public String createGroup( @PostMapping("/addToGroup") public String addToGroup( - @NotNull @RequestParam("secret") String secret, @NotNull @RequestParam("groupId") Integer groupId, @NotNull @RequestParam("containerId") String containerId, @NotNull @RequestParam("server") String server) { - security.checkSecret(secret); Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); @@ -63,10 +58,8 @@ public String addToGroup( @GetMapping("/getGroup") public List getGroup( - @NotNull @RequestParam("secret") String secret, @NotNull @RequestParam("groupId") Integer groupId, @RequestParam("server") String server) { - security.checkSecret(secret); Optional groups = groupRepository.findById(groupId); if (groups.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); @@ -76,9 +69,7 @@ public List getGroup( @DeleteMapping("/deleteGroup") public String deleteGroup( - @NotNull @RequestParam("secret") String secret, @NotNull @RequestParam("groupId") Integer groupId) { - security.checkSecret(secret); Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); @@ -89,16 +80,13 @@ public String deleteGroup( @GetMapping("/groups") public List getAllGroups(@NotNull @RequestParam("secret") String secret) { - security.checkSecret(secret); return groupRepository.findAll(); } @PostMapping("/renameGroup") public String renameGroup( - @NotNull @RequestParam("secret") String secret, @NotNull @RequestParam("groupId") Integer groupId, @NotNull @RequestParam("renameTo") String renameString) { - security.checkSecret(secret); Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); @@ -110,11 +98,9 @@ public String renameGroup( @DeleteMapping("deleteFromGroup") public String deleteFromGroup( - @RequestParam("secret") String secret, @RequestParam("groupId") Integer groupId, @RequestParam("containerId") String name, @RequestParam("server") String server) { - security.checkSecret(secret); Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); @@ -137,8 +123,7 @@ public String deleteFromGroup( } @Autowired - public GroupController(Security security, ContainerRepository containerRepository, GroupRepository groupRepository) { - this.security = security; + public GroupController(ContainerRepository containerRepository, GroupRepository groupRepository) { this.containerRepository = containerRepository; this.groupRepository = groupRepository; } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java b/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java new file mode 100644 index 0000000..de79b88 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java @@ -0,0 +1,83 @@ +package com.sms.stockmanagementsystem.project; + +import com.sms.stockmanagementsystem.project.data.authentication.Privilege; +import com.sms.stockmanagementsystem.project.data.authentication.Role; +import com.sms.stockmanagementsystem.project.data.authentication.User; +import com.sms.stockmanagementsystem.project.repositories.authentication.RoleRepository; +import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +@Service("userDetailsService") +@Transactional +public class MyUserDetailsService implements UserDetailsService { + + @Autowired + private UserRepository userRepository; + +// @Autowired +// private IUserService service; + + @Autowired + private MessageSource messages; + + @Autowired + private RoleRepository roleRepository; + + @Override + public UserDetails loadUserByUsername(String email) + throws UsernameNotFoundException { + + User user = userRepository.findByEmail(email); + if (user == null) { + return new org.springframework.security.core.userdetails.User( + " ", " ", true, true, true, true, + getAuthorities(Arrays.asList( + roleRepository.findByName("ROLE_USER")))); + } + + return new org.springframework.security.core.userdetails.User( + user.getEmail(), user.getPassword(), user.isEnabled(), true, true, + true, getAuthorities(user.getRoles())); + } + + private Collection getAuthorities( + Collection roles) { + + return getGrantedAuthorities(getPrivileges(roles)); + } + + private List getPrivileges(Collection roles) { + + List privileges = new ArrayList<>(); + List collection = new ArrayList<>(); + for (Role role : roles) { + privileges.add(role.getName()); + collection.addAll(role.getPrivileges()); + } + for (Privilege item : collection) { + privileges.add(item.getName()); + } + return privileges; + } + + private List getGrantedAuthorities(List privileges) { + List authorities = new ArrayList<>(); + for (String privilege : privileges) { + authorities.add(new SimpleGrantedAuthority(privilege)); + } + return authorities; + } +} \ No newline at end of file diff --git a/src/main/java/com/sms/stockmanagementsystem/project/Security.java b/src/main/java/com/sms/stockmanagementsystem/project/Security.java deleted file mode 100644 index 6287e86..0000000 --- a/src/main/java/com/sms/stockmanagementsystem/project/Security.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.sms.stockmanagementsystem.project; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.web.server.ResponseStatusException; - -@Service -public class Security { - - Environment environment; - - public void checkSecret(String secret) { - if (!secret.equals(environment.getProperty("secret"))) { - throw new ResponseStatusException(HttpStatus.FORBIDDEN); - } - } - - - @Autowired - public Security(Environment environment) { - this.environment = environment; - } -} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java b/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java new file mode 100644 index 0000000..2ad936a --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java @@ -0,0 +1,93 @@ +package com.sms.stockmanagementsystem.project; + +import com.sms.stockmanagementsystem.project.data.authentication.Privilege; +import com.sms.stockmanagementsystem.project.data.authentication.Role; +import com.sms.stockmanagementsystem.project.data.authentication.User; +import com.sms.stockmanagementsystem.project.repositories.authentication.PrivilegeRepository; +import com.sms.stockmanagementsystem.project.repositories.authentication.RoleRepository; +import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +@Component +public class SetupDataLoader implements + ApplicationListener { + + boolean alreadySetup = false; + + @Autowired + private UserRepository userRepository; + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private PrivilegeRepository privilegeRepository; + +// @Autowired +// private PasswordEncoder passwordEncoder; + + @Override + @Transactional + public void onApplicationEvent(ContextRefreshedEvent event) { + + if (alreadySetup) + return; + Privilege readPrivilege + = createPrivilegeIfNotFound("READ_PRIVILEGE"); + Privilege writePrivilege + = createPrivilegeIfNotFound("WRITE_PRIVILEGE"); + + List adminPrivileges = Arrays.asList( + readPrivilege, writePrivilege); + createRoleIfNotFound("ROLE_ADMIN", adminPrivileges); + createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege)); + + + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + Role adminRole = roleRepository.findByName("ROLE_ADMIN"); + User user = new User(); + user.setFirstName("Test"); + user.setLastName("Test"); + user.setPassword("{bcrypt}" + passwordEncoder.encode("test")); + user.setEmail("test@test.com"); + user.setRoles(Arrays.asList(adminRole)); + user.setEnabled(true); + userRepository.save(user); + + alreadySetup = true; + } + + @Transactional + Privilege createPrivilegeIfNotFound(String name) { + + Privilege privilege = privilegeRepository.findByName(name); + if (privilege == null) { + privilege = new Privilege(name); + privilegeRepository.save(privilege); + } + return privilege; + } + + @Transactional + Role createRoleIfNotFound( + String name, Collection privileges) { + + Role role = roleRepository.findByName(name); + if (role == null) { + role = new Role(name); + role.setPrivileges(privileges); + roleRepository.save(role); + } + return role; + } +} \ No newline at end of file diff --git a/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java b/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java index 77e5255..89cd98b 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java @@ -5,6 +5,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; +import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -28,4 +31,20 @@ public void addCorsMappings(@NotNull CorsRegistry registry) { } }; } + + @Bean + public RoleHierarchy roleHierarchy() { + RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); + String hierarchy = "ROLE_ADMIN > ROLE_STAFF \n ROLE_STAFF > ROLE_USER"; + roleHierarchy.setHierarchy(hierarchy); + return roleHierarchy; + } + + @Bean + public DefaultWebSecurityExpressionHandler customWebSecurityExpressionHandler() { + DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler(); + expressionHandler.setRoleHierarchy(roleHierarchy()); + return expressionHandler; + } + } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java new file mode 100644 index 0000000..8f91402 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java @@ -0,0 +1,48 @@ +package com.sms.stockmanagementsystem.project.data.authentication; + +import jakarta.persistence.*; + +import java.util.Collection; + +@Entity +@Table(name = "privileges") +public class Privilege { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + + @ManyToMany(mappedBy = "privileges") + private Collection roles; + + public Privilege(String name) { + } + + public Privilege() {} + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Collection getRoles() { + return roles; + } + + public void setRoles(Collection roles) { + this.roles = roles; + } +} \ No newline at end of file diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java new file mode 100644 index 0000000..d53f3ef --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java @@ -0,0 +1,64 @@ +package com.sms.stockmanagementsystem.project.data.authentication; + +import jakarta.persistence.*; + +import java.util.Collection; + +@Entity +@Table(name = "roles") +public class Role { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + @ManyToMany(mappedBy = "roles") + private Collection users; + + @ManyToMany + @JoinTable( + name = "roles_privileges", + joinColumns = @JoinColumn( + name = "role_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn( + name = "privilege_id", referencedColumnName = "id")) + private Collection privileges; + + public Role() {} + + public Role(String name) { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Collection getUsers() { + return users; + } + + public void setUsers(Collection users) { + this.users = users; + } + + public Collection getPrivileges() { + return privileges; + } + + public void setPrivileges(Collection privileges) { + this.privileges = privileges; + } +} \ No newline at end of file diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java new file mode 100644 index 0000000..7433762 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java @@ -0,0 +1,97 @@ +package com.sms.stockmanagementsystem.project.data.authentication; + +import jakarta.persistence.*; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +@Entity +@Table(name = "users") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String firstName; + private String lastName; + private String email; + private String password; + private boolean enabled; + private boolean tokenExpired; + + @ManyToMany + @JoinTable( + name = "users_roles", + joinColumns = @JoinColumn( + name = "user_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn( + name = "role_id", referencedColumnName = "id")) + private Collection roles; + + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isTokenExpired() { + return tokenExpired; + } + + public void setTokenExpired(boolean tokenExpired) { + this.tokenExpired = tokenExpired; + } + + public Collection getRoles() { + return roles; + } + + public void setRoles(Collection roles) { + this.roles = roles; + } +} \ No newline at end of file diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java new file mode 100644 index 0000000..46bc316 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java @@ -0,0 +1,11 @@ +package com.sms.stockmanagementsystem.project.repositories.authentication; + +import com.sms.stockmanagementsystem.project.data.authentication.Privilege; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PrivilegeRepository extends JpaRepository { + + Privilege findByName(String name); +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java new file mode 100644 index 0000000..fd85dd0 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java @@ -0,0 +1,12 @@ +package com.sms.stockmanagementsystem.project.repositories.authentication; + +import com.sms.stockmanagementsystem.project.data.authentication.Role; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + + +@Repository +public interface RoleRepository extends JpaRepository { + + Role findByName(String name); +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java new file mode 100644 index 0000000..c244c7e --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java @@ -0,0 +1,13 @@ +package com.sms.stockmanagementsystem.project.repositories.authentication; + + +import com.sms.stockmanagementsystem.project.data.authentication.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + + User findByEmail(String email); + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 04b4ef9..a7a6a39 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,5 +16,8 @@ server: error: include-message: ALWAYS +logging.level: + org.springframework.security: TRACE + secret: ${SECRET} \ No newline at end of file From f7f56f594014bf4ae4476bcb2637ab6a984faa73 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:11:59 +0100 Subject: [PATCH 02/13] --wip-- [skip ci] --- .../project/data/Container.java | 1 + .../data/authentication/Privilege.java | 31 ++------ .../project/data/authentication/Role.java | 39 ++--------- .../project/data/authentication/User.java | 70 +------------------ 4 files changed, 14 insertions(+), 127 deletions(-) diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java b/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java index 9ad7b99..bb615ba 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java @@ -36,6 +36,7 @@ public class Container { public Container() {} + public Container(String name, LocalDateTime time, String username, String data) { this.name = name; this.updatedAt = time; diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java index 8f91402..e94dfe4 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java @@ -1,11 +1,15 @@ package com.sms.stockmanagementsystem.project.data.authentication; import jakarta.persistence.*; +import lombok.Data; +import lombok.NoArgsConstructor; import java.util.Collection; +@Data @Entity @Table(name = "privileges") +@NoArgsConstructor public class Privilege { @Id @@ -18,31 +22,6 @@ public class Privilege { private Collection roles; public Privilege(String name) { - } - - public Privilege() {} - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { this.name = name; } - - public Collection getRoles() { - return roles; - } - - public void setRoles(Collection roles) { - this.roles = roles; - } -} \ No newline at end of file +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java index d53f3ef..62f4441 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java @@ -1,11 +1,15 @@ package com.sms.stockmanagementsystem.project.data.authentication; import jakarta.persistence.*; +import lombok.Data; +import lombok.NoArgsConstructor; import java.util.Collection; +@Data @Entity @Table(name = "roles") +@NoArgsConstructor public class Role { @Id @@ -25,40 +29,7 @@ public class Role { name = "privilege_id", referencedColumnName = "id")) private Collection privileges; - public Role() {} - public Role(String name) { - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { this.name = name; } - - public Collection getUsers() { - return users; - } - - public void setUsers(Collection users) { - this.users = users; - } - - public Collection getPrivileges() { - return privileges; - } - - public void setPrivileges(Collection privileges) { - this.privileges = privileges; - } -} \ No newline at end of file +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java index 7433762..18df1d4 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java @@ -1,10 +1,11 @@ package com.sms.stockmanagementsystem.project.data.authentication; import jakarta.persistence.*; -import org.springframework.security.core.GrantedAuthority; +import lombok.Data; import java.util.Collection; +@Data @Entity @Table(name = "users") public class User { @@ -29,69 +30,4 @@ public class User { name = "role_id", referencedColumnName = "id")) private Collection roles; - - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isTokenExpired() { - return tokenExpired; - } - - public void setTokenExpired(boolean tokenExpired) { - this.tokenExpired = tokenExpired; - } - - public Collection getRoles() { - return roles; - } - - public void setRoles(Collection roles) { - this.roles = roles; - } -} \ No newline at end of file +} From c481a8840a335d7050e12accd7a29e36904ae342 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Fri, 21 Feb 2025 19:43:02 +0100 Subject: [PATCH 03/13] --wip-- [skip ci] --- build.gradle | 1 + .../project/ContainerController.java | 2 +- .../project/GroupController.java | 2 +- .../project/MyUserDetailsService.java | 5 +- .../project/SecurityConfig.java | 31 +++++ .../project/SetupDataLoader.java | 114 +++++++++--------- .../project/data/authentication/Role.java | 1 + src/main/resources/application.yml | 5 +- 8 files changed, 93 insertions(+), 68 deletions(-) create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java diff --git a/build.gradle b/build.gradle index afcd863..1d1c867 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation 'org.jetbrains:annotations:15.0' implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.3.1' implementation 'me.paulschwarz:spring-dotenv:4.0.0' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java b/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java index d0fed33..95639de 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java @@ -17,7 +17,7 @@ import java.util.List; @RestController -@RequestMapping("/sms/api") +@RequestMapping("/api") public class ContainerController { private final ContainerRepository containerRepository; diff --git a/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java b/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java index 9b3d3e7..aa964c4 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java @@ -15,7 +15,7 @@ import java.util.Optional; @RestController -@RequestMapping("/sms/api") +@RequestMapping("/api") public class GroupController { diff --git a/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java b/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java index de79b88..0e161c2 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java @@ -30,9 +30,6 @@ public class MyUserDetailsService implements UserDetailsService { // @Autowired // private IUserService service; - @Autowired - private MessageSource messages; - @Autowired private RoleRepository roleRepository; @@ -80,4 +77,4 @@ private List getGrantedAuthorities(List privileges) { } return authorities; } -} \ No newline at end of file +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java b/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java new file mode 100644 index 0000000..95425b1 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java @@ -0,0 +1,31 @@ +package com.sms.stockmanagementsystem.project; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) + throws Exception { + http.authorizeHttpRequests((authorize) -> { + authorize.requestMatchers("/").permitAll(); + authorize.requestMatchers("/resources/**").permitAll(); + authorize.requestMatchers("/error").permitAll(); + authorize.anyRequest().authenticated(); + }) + .formLogin(Customizer.withDefaults()) + // .csrf(csrf + // -> csrf.csrfTokenRepository( + // CookieCsrfTokenRepository.withHttpOnlyFalse())); + ; + return http.build(); + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java b/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java index 2ad936a..20aadd1 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java @@ -7,87 +7,81 @@ import com.sms.stockmanagementsystem.project.repositories.authentication.RoleRepository; import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; import jakarta.transaction.Transactional; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - @Component -public class SetupDataLoader implements - ApplicationListener { +public class SetupDataLoader + implements ApplicationListener { - boolean alreadySetup = false; + boolean alreadySetup = false; - @Autowired - private UserRepository userRepository; + @Autowired private UserRepository userRepository; - @Autowired - private RoleRepository roleRepository; + @Autowired private RoleRepository roleRepository; - @Autowired - private PrivilegeRepository privilegeRepository; + @Autowired private PrivilegeRepository privilegeRepository; -// @Autowired -// private PasswordEncoder passwordEncoder; + // @Autowired + // private PasswordEncoder passwordEncoder; - @Override - @Transactional - public void onApplicationEvent(ContextRefreshedEvent event) { + @Override + @Transactional + public void onApplicationEvent(ContextRefreshedEvent event) { - if (alreadySetup) - return; - Privilege readPrivilege - = createPrivilegeIfNotFound("READ_PRIVILEGE"); - Privilege writePrivilege - = createPrivilegeIfNotFound("WRITE_PRIVILEGE"); + Privilege readPrivilege = createPrivilegeIfNotFound("READ_PRIVILEGE"); + Privilege writePrivilege = createPrivilegeIfNotFound("WRITE_PRIVILEGE"); - List adminPrivileges = Arrays.asList( - readPrivilege, writePrivilege); - createRoleIfNotFound("ROLE_ADMIN", adminPrivileges); - createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege)); + List adminPrivileges = + Arrays.asList(readPrivilege, writePrivilege); + createRoleIfNotFound("ROLE_ADMIN", adminPrivileges); + createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege)); + if (userRepository.findByEmail("testuser") != null) { + return; + } - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - Role adminRole = roleRepository.findByName("ROLE_ADMIN"); - User user = new User(); - user.setFirstName("Test"); - user.setLastName("Test"); - user.setPassword("{bcrypt}" + passwordEncoder.encode("test")); - user.setEmail("test@test.com"); - user.setRoles(Arrays.asList(adminRole)); - user.setEnabled(true); - userRepository.save(user); + Role adminRole = roleRepository.findByName("ROLE_USER"); + User user = new User(); + user.setFirstName("Test"); + user.setLastName("Test"); + user.setPassword("{bcrypt}" + passwordEncoder.encode("test")); + user.setEmail("testuser"); + user.setRoles(Arrays.asList(adminRole)); + user.setEnabled(true); + userRepository.save(user); - alreadySetup = true; - } + alreadySetup = true; + } - @Transactional - Privilege createPrivilegeIfNotFound(String name) { + @Transactional + Privilege createPrivilegeIfNotFound(String name) { - Privilege privilege = privilegeRepository.findByName(name); - if (privilege == null) { - privilege = new Privilege(name); - privilegeRepository.save(privilege); - } - return privilege; + Privilege privilege = privilegeRepository.findByName(name); + if (privilege == null) { + privilege = new Privilege(name); + privilegeRepository.save(privilege); } + return privilege; + } + + @Transactional + Role createRoleIfNotFound(String name, Collection privileges) { - @Transactional - Role createRoleIfNotFound( - String name, Collection privileges) { - - Role role = roleRepository.findByName(name); - if (role == null) { - role = new Role(name); - role.setPrivileges(privileges); - roleRepository.save(role); - } - return role; + Role role = roleRepository.findByName(name); + if (role == null) { + role = new Role(name); + role.setPrivileges(privileges); + roleRepository.save(role); } -} \ No newline at end of file + return role; + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java index 62f4441..0a1de44 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java @@ -17,6 +17,7 @@ public class Role { private Long id; private String name; + @ManyToMany(mappedBy = "roles") private Collection users; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a7a6a39..ca50b3e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,9 +15,10 @@ spring: server: error: include-message: ALWAYS + servlet: + context-path: /sms/ logging.level: org.springframework.security: TRACE - -secret: ${SECRET} \ No newline at end of file +secret: ${SECRET} From a71dd516965060103d0d741b1e95c58dfaac7b69 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Sat, 22 Feb 2025 01:02:23 +0100 Subject: [PATCH 04/13] --wip-- --- .../project/MyUserDetailsService.java | 80 ------------------ .../project/SecurityConfig.java | 17 ++-- .../project/SetupDataLoader.java | 11 +-- .../StockManagementSystemApplication.java | 14 ++-- .../v1}/ContainerController.java | 71 +++++++++------- .../{ => controllers/v1}/GroupController.java | 81 ++++++++++--------- .../project/data/Container.java | 24 ++---- .../project/data/Group.java | 14 ++-- .../data/authentication/Privilege.java | 16 ++-- .../project/data/authentication/Role.java | 31 +++---- .../project/data/authentication/User.java | 35 ++++---- .../repositories/ContainerRepository.java | 3 +- .../project/repositories/GroupRepository.java | 3 +- .../authentication/PrivilegeRepository.java | 2 +- .../authentication/RoleRepository.java | 3 +- .../authentication/UserRepository.java | 7 +- .../services/SmsUserDetailsService.java | 78 ++++++++++++++++++ .../project/services/UserService.java | 58 +++++++++++++ 18 files changed, 298 insertions(+), 250 deletions(-) delete mode 100644 src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java rename src/main/java/com/sms/stockmanagementsystem/project/{ => controllers/v1}/ContainerController.java (57%) rename src/main/java/com/sms/stockmanagementsystem/project/{ => controllers/v1}/GroupController.java (63%) create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/services/SmsUserDetailsService.java create mode 100644 src/main/java/com/sms/stockmanagementsystem/project/services/UserService.java diff --git a/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java b/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java deleted file mode 100644 index 0e161c2..0000000 --- a/src/main/java/com/sms/stockmanagementsystem/project/MyUserDetailsService.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.sms.stockmanagementsystem.project; - -import com.sms.stockmanagementsystem.project.data.authentication.Privilege; -import com.sms.stockmanagementsystem.project.data.authentication.Role; -import com.sms.stockmanagementsystem.project.data.authentication.User; -import com.sms.stockmanagementsystem.project.repositories.authentication.RoleRepository; -import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; -import jakarta.transaction.Transactional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -@Service("userDetailsService") -@Transactional -public class MyUserDetailsService implements UserDetailsService { - - @Autowired - private UserRepository userRepository; - -// @Autowired -// private IUserService service; - - @Autowired - private RoleRepository roleRepository; - - @Override - public UserDetails loadUserByUsername(String email) - throws UsernameNotFoundException { - - User user = userRepository.findByEmail(email); - if (user == null) { - return new org.springframework.security.core.userdetails.User( - " ", " ", true, true, true, true, - getAuthorities(Arrays.asList( - roleRepository.findByName("ROLE_USER")))); - } - - return new org.springframework.security.core.userdetails.User( - user.getEmail(), user.getPassword(), user.isEnabled(), true, true, - true, getAuthorities(user.getRoles())); - } - - private Collection getAuthorities( - Collection roles) { - - return getGrantedAuthorities(getPrivileges(roles)); - } - - private List getPrivileges(Collection roles) { - - List privileges = new ArrayList<>(); - List collection = new ArrayList<>(); - for (Role role : roles) { - privileges.add(role.getName()); - collection.addAll(role.getPrivileges()); - } - for (Privilege item : collection) { - privileges.add(item.getName()); - } - return privileges; - } - - private List getGrantedAuthorities(List privileges) { - List authorities = new ArrayList<>(); - for (String privilege : privileges) { - authorities.add(new SimpleGrantedAuthority(privilege)); - } - return authorities; - } -} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java b/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java index 95425b1..916fcbe 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java @@ -5,6 +5,8 @@ import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @@ -17,15 +19,20 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorize) -> { authorize.requestMatchers("/").permitAll(); - authorize.requestMatchers("/resources/**").permitAll(); authorize.requestMatchers("/error").permitAll(); authorize.anyRequest().authenticated(); }) .formLogin(Customizer.withDefaults()) - // .csrf(csrf - // -> csrf.csrfTokenRepository( - // CookieCsrfTokenRepository.withHttpOnlyFalse())); - ; + .logout(logout -> logout.permitAll()) + .csrf(csrf + -> csrf.csrfTokenRepository( + CookieCsrfTokenRepository.withHttpOnlyFalse())); + ; return http.build(); } + + @Bean + public PasswordEncoder encoder() { + return new BCryptPasswordEncoder(); + } } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java b/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java index 20aadd1..bd35efc 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java @@ -14,6 +14,7 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; @Component @@ -28,6 +29,8 @@ public class SetupDataLoader @Autowired private PrivilegeRepository privilegeRepository; + @Autowired PasswordEncoder passwordEncoder; + // @Autowired // private PasswordEncoder passwordEncoder; @@ -43,18 +46,16 @@ public void onApplicationEvent(ContextRefreshedEvent event) { createRoleIfNotFound("ROLE_ADMIN", adminPrivileges); createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege)); - if (userRepository.findByEmail("testuser") != null) { + if (userRepository.findByUsername("user").isPresent()) { return; } - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - Role adminRole = roleRepository.findByName("ROLE_USER"); User user = new User(); user.setFirstName("Test"); user.setLastName("Test"); - user.setPassword("{bcrypt}" + passwordEncoder.encode("test")); - user.setEmail("testuser"); + user.setPassword(passwordEncoder.encode("test")); + user.setUsername("user"); user.setRoles(Arrays.asList(adminRole)); user.setEnabled(true); userRepository.save(user); diff --git a/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java b/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java index 89cd98b..13d64f2 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java @@ -18,8 +18,7 @@ public static void main(String[] args) { SpringApplication.run(StockManagementSystemApplication.class, args); } - @Override - public void run(String... args) { // NOSONAR + @Override public void run(String... args) { // NOSONAR } @Bean @@ -34,17 +33,18 @@ public void addCorsMappings(@NotNull CorsRegistry registry) { @Bean public RoleHierarchy roleHierarchy() { - RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); String hierarchy = "ROLE_ADMIN > ROLE_STAFF \n ROLE_STAFF > ROLE_USER"; - roleHierarchy.setHierarchy(hierarchy); + RoleHierarchyImpl roleHierarchy = + RoleHierarchyImpl.fromHierarchy(hierarchy); return roleHierarchy; } @Bean - public DefaultWebSecurityExpressionHandler customWebSecurityExpressionHandler() { - DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler(); + public DefaultWebSecurityExpressionHandler + customWebSecurityExpressionHandler() { + DefaultWebSecurityExpressionHandler expressionHandler = + new DefaultWebSecurityExpressionHandler(); expressionHandler.setRoleHierarchy(roleHierarchy()); return expressionHandler; } - } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java b/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/ContainerController.java similarity index 57% rename from src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java rename to src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/ContainerController.java index 95639de..76c3e38 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/ContainerController.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/ContainerController.java @@ -1,29 +1,32 @@ -package com.sms.stockmanagementsystem.project; +package com.sms.stockmanagementsystem.project.controllers.v1; import com.sms.stockmanagementsystem.project.data.Container; import com.sms.stockmanagementsystem.project.data.Group; import com.sms.stockmanagementsystem.project.repositories.ContainerRepository; import com.sms.stockmanagementsystem.project.repositories.GroupRepository; +import java.time.LocalDateTime; +import java.util.List; import org.jetbrains.annotations.NotNull; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.HttpServerErrorException; -import java.time.LocalDateTime; -import java.util.List; - @RestController -@RequestMapping("/api") +@RequestMapping("/api/v1") +@EnableMethodSecurity(securedEnabled = true) public class ContainerController { private final ContainerRepository containerRepository; private final GroupRepository groupRepository; + @Secured({"WRITE_PRIVILEGE"}) @GetMapping("/health") public String health() { return "alive"; @@ -31,20 +34,21 @@ public String health() { @CrossOrigin @PostMapping("/container") - public Container setContainerDetails( - @RequestBody String data, - @NotNull @RequestParam("server") String serverName) + public Container + setContainerDetails(@RequestBody String data, + @NotNull @RequestParam("server") String serverName) throws ParseException { JSONParser parser = new JSONParser(); - JSONObject jsonObject = (JSONObject) parser.parse(data); + JSONObject jsonObject = (JSONObject)parser.parse(data); - String name = (String) jsonObject.get("name"); - String user = (String) jsonObject.get("updatedBy"); - String containerData = (String) jsonObject.get("data"); + String name = (String)jsonObject.get("name"); + String user = (String)jsonObject.get("updatedBy"); + String containerData = (String)jsonObject.get("data"); Container item; - List containers = containerRepository.findByNameAndServer(name, serverName); + List containers = + containerRepository.findByNameAndServer(name, serverName); if (containers.isEmpty()) { item = new Container(name, user, containerData, serverName); } else { @@ -60,10 +64,11 @@ public Container setContainerDetails( @CrossOrigin @GetMapping("/container") - public Container getContainerDetails( - @NotNull @RequestParam("containerId") String name, - @NotNull @RequestParam("server") String server) { - List item = containerRepository.findByNameAndServer(name, server); + public Container + getContainerDetails(@NotNull @RequestParam("containerId") String name, + @NotNull @RequestParam("server") String server) { + List item = + containerRepository.findByNameAndServer(name, server); if (item.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST); } @@ -72,12 +77,14 @@ public Container getContainerDetails( @CrossOrigin @DeleteMapping("/container") - public String deleteContainerItem( - @NotNull @RequestParam("containerId") String name, - @NotNull @RequestParam("server") String server) { - List containerList = containerRepository.findByNameAndServer(name, server); + public String + deleteContainerItem(@NotNull @RequestParam("containerId") String name, + @NotNull @RequestParam("server") String server) { + List containerList = + containerRepository.findByNameAndServer(name, server); if (containerList.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "container does not exist"); } Container container = containerList.get(0); for (Group group : container.getGroups()) { @@ -92,25 +99,27 @@ public String deleteContainerItem( @GetMapping("/getContainers") public List getAllContainers( @RequestParam(value = "server", required = false) String server) { - return server != null - ? containerRepository.findByServer(server) - : containerRepository.findAll(); + return server != null ? containerRepository.findByServer(server) + : containerRepository.findAll(); } @GetMapping("/getContainerGroups") - public List getContainerGroups( - @RequestParam("server") String server, - @RequestParam("containerId") String name) { - List containerList = containerRepository.findByNameAndServer(name, server); + public List + getContainerGroups(@RequestParam("server") String server, + @RequestParam("containerId") String name) { + List containerList = + containerRepository.findByNameAndServer(name, server); if (containerList.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "container does not exist"); } Container container = containerList.get(0); return container.getGroups(); } @Autowired - public ContainerController(ContainerRepository containerRepository, GroupRepository groupRepository) { + public ContainerController(ContainerRepository containerRepository, + GroupRepository groupRepository) { this.containerRepository = containerRepository; this.groupRepository = groupRepository; } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java b/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/GroupController.java similarity index 63% rename from src/main/java/com/sms/stockmanagementsystem/project/GroupController.java rename to src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/GroupController.java index aa964c4..70351ca 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/GroupController.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/GroupController.java @@ -1,9 +1,11 @@ -package com.sms.stockmanagementsystem.project; +package com.sms.stockmanagementsystem.project.controllers.v1; import com.sms.stockmanagementsystem.project.data.Container; import com.sms.stockmanagementsystem.project.data.Group; import com.sms.stockmanagementsystem.project.repositories.ContainerRepository; import com.sms.stockmanagementsystem.project.repositories.GroupRepository; +import java.util.List; +import java.util.Optional; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -11,24 +13,20 @@ import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.server.ResponseStatusException; -import java.util.List; -import java.util.Optional; - @RestController -@RequestMapping("/api") +@RequestMapping("/api/v1") public class GroupController { - private final GroupRepository groupRepository; private final ContainerRepository containerRepository; @PostMapping("/createGroup") - public String createGroup( - @NotNull @RequestParam("name") String name, - @RequestParam("user") String user) { + public String createGroup(@NotNull @RequestParam("name") String name, + @RequestParam("user") String user) { if (!groupRepository.findByName(name).isEmpty()) { - throw new ResponseStatusException(HttpStatus.CONFLICT, "group already exists"); + throw new ResponseStatusException(HttpStatus.CONFLICT, + "group already exists"); } Group group = new Group(name, user); groupRepository.save(group); @@ -36,19 +34,21 @@ public String createGroup( } @PostMapping("/addToGroup") - public String addToGroup( - @NotNull @RequestParam("groupId") Integer groupId, - @NotNull @RequestParam("containerId") String containerId, - @NotNull @RequestParam("server") String server) { + public String + addToGroup(@NotNull @RequestParam("groupId") Integer groupId, + @NotNull @RequestParam("containerId") String containerId, + @NotNull @RequestParam("server") String server) { Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "group does not exist"); } Group group = groupOptional.get(); List containerOptional = containerRepository.findByNameAndServer(containerId, server); if (containerOptional.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "id does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "id does not exist"); } Container container = containerOptional.get(0); group.addContainer(container); @@ -57,39 +57,42 @@ public String addToGroup( } @GetMapping("/getGroup") - public List getGroup( - @NotNull @RequestParam("groupId") Integer groupId, - @RequestParam("server") String server) { + public List + getGroup(@NotNull @RequestParam("groupId") Integer groupId, + @RequestParam("server") String server) { Optional groups = groupRepository.findById(groupId); if (groups.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "group does not exist"); } return groups.get().getContainers(server); } @DeleteMapping("/deleteGroup") - public String deleteGroup( - @NotNull @RequestParam("groupId") Integer groupId) { + public String deleteGroup(@NotNull @RequestParam("groupId") Integer groupId) { Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "group does not exist"); } groupRepository.delete(groupOptional.get()); return "success"; } @GetMapping("/groups") - public List getAllGroups(@NotNull @RequestParam("secret") String secret) { + public List + getAllGroups(@NotNull @RequestParam("secret") String secret) { return groupRepository.findAll(); } @PostMapping("/renameGroup") - public String renameGroup( - @NotNull @RequestParam("groupId") Integer groupId, - @NotNull @RequestParam("renameTo") String renameString) { + public String + renameGroup(@NotNull @RequestParam("groupId") Integer groupId, + @NotNull @RequestParam("renameTo") String renameString) { Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "group does not exist"); } groupOptional.get().setName(renameString); groupRepository.save(groupOptional.get()); @@ -97,23 +100,26 @@ public String renameGroup( } @DeleteMapping("deleteFromGroup") - public String deleteFromGroup( - @RequestParam("groupId") Integer groupId, - @RequestParam("containerId") String name, - @RequestParam("server") String server) { + public String deleteFromGroup(@RequestParam("groupId") Integer groupId, + @RequestParam("containerId") String name, + @RequestParam("server") String server) { Optional groupOptional = groupRepository.findById(groupId); if (groupOptional.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "group does not exist"); } - List containerOptional = containerRepository.findByNameAndServer(name, server); + List containerOptional = + containerRepository.findByNameAndServer(name, server); if (containerOptional.isEmpty()) { throw new HttpServerErrorException( - HttpStatus.BAD_REQUEST, "containerid does not exist (on this server?)"); + HttpStatus.BAD_REQUEST, + "containerid does not exist (on this server?)"); } Group group = groupOptional.get(); Container container = containerOptional.get(0); if (!group.getContainers().contains(container)) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container is not in group"); + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, + "container is not in group"); } group.removeContainer(container); groupRepository.save(group); @@ -123,7 +129,8 @@ public String deleteFromGroup( } @Autowired - public GroupController(ContainerRepository containerRepository, GroupRepository groupRepository) { + public GroupController(ContainerRepository containerRepository, + GroupRepository groupRepository) { this.containerRepository = containerRepository; this.groupRepository = groupRepository; } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java b/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java index bb615ba..630b835 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java @@ -2,11 +2,10 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Data; - import java.time.LocalDateTime; import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; @Data @Entity @@ -14,9 +13,7 @@ @Table(name = "Container") public class Container { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; @@ -25,19 +22,16 @@ public class Container { private LocalDateTime updatedAt; private String updatedBy; - @Column(columnDefinition = "TEXT") - private String data; + @Column(columnDefinition = "TEXT") private String data; - @JsonIgnore - @ManyToMany(mappedBy = "containers") - private List groups; + @JsonIgnore @ManyToMany(mappedBy = "containers") private List groups; private String server; public Container() {} - - public Container(String name, LocalDateTime time, String username, String data) { + public Container(String name, LocalDateTime time, String username, + String data) { this.name = name; this.updatedAt = time; this.updatedBy = username; @@ -54,7 +48,5 @@ public Container(String name, String user, String data, String server) { this.createdBy = user; } - public void removeGroup(Group group) { - this.groups.remove(group); - } + public void removeGroup(Group group) { this.groups.remove(group); } } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/Group.java b/src/main/java/com/sms/stockmanagementsystem/project/data/Group.java index a0ea279..fb37c40 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/Group.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/Group.java @@ -2,19 +2,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; -import lombok.Data; - import java.time.LocalDateTime; import java.util.List; +import lombok.Data; @Entity @Data @Table(name = "Groups") public class Group { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Integer id; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private LocalDateTime createdAt; private String createdBy; @@ -22,10 +19,9 @@ public class Group { @JsonIgnore @ManyToMany - @JoinTable( - name = "group_containers", - joinColumns = @JoinColumn(name = "group_id"), - inverseJoinColumns = @JoinColumn(name = "container_id")) + @JoinTable(name = "group_containers", + joinColumns = @JoinColumn(name = "group_id"), + inverseJoinColumns = @JoinColumn(name = "container_id")) private List containers; public Group() {} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java index e94dfe4..c941d42 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java @@ -1,27 +1,21 @@ package com.sms.stockmanagementsystem.project.data.authentication; import jakarta.persistence.*; +import java.util.Collection; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.Collection; - @Data @Entity @Table(name = "privileges") @NoArgsConstructor public class Privilege { - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - private String name; + private String name; - @ManyToMany(mappedBy = "privileges") - private Collection roles; + @ManyToMany(mappedBy = "privileges") private Collection roles; - public Privilege(String name) { - this.name = name; - } + public Privilege(String name) { this.name = name; } } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java index 0a1de44..c5db04d 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java @@ -1,36 +1,29 @@ package com.sms.stockmanagementsystem.project.data.authentication; import jakarta.persistence.*; +import java.util.Collection; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.Collection; - @Data @Entity @Table(name = "roles") @NoArgsConstructor public class Role { - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - private String name; + private String name; - @ManyToMany(mappedBy = "roles") - private Collection users; + @ManyToMany(mappedBy = "roles") private Collection users; - @ManyToMany - @JoinTable( - name = "roles_privileges", - joinColumns = @JoinColumn( - name = "role_id", referencedColumnName = "id"), - inverseJoinColumns = @JoinColumn( - name = "privilege_id", referencedColumnName = "id")) - private Collection privileges; + @ManyToMany + @JoinTable( + name = "roles_privileges", + joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"), + inverseJoinColumns = + @JoinColumn(name = "privilege_id", referencedColumnName = "id")) + private Collection privileges; - public Role(String name) { - this.name = name; - } + public Role(String name) { this.name = name; } } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java index 18df1d4..bae9d11 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java @@ -1,33 +1,28 @@ package com.sms.stockmanagementsystem.project.data.authentication; import jakarta.persistence.*; -import lombok.Data; - import java.util.Collection; +import lombok.Data; @Data @Entity @Table(name = "users") public class User { - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; - - private String firstName; - private String lastName; - private String email; - private String password; - private boolean enabled; - private boolean tokenExpired; + @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - @ManyToMany - @JoinTable( - name = "users_roles", - joinColumns = @JoinColumn( - name = "user_id", referencedColumnName = "id"), - inverseJoinColumns = @JoinColumn( - name = "role_id", referencedColumnName = "id")) - private Collection roles; + private String firstName; + private String lastName; + private String username; + private String password; + private boolean enabled; + private boolean tokenExpired; + @ManyToMany + @JoinTable(name = "users_roles", + joinColumns = + @JoinColumn(name = "user_id", referencedColumnName = "id"), + inverseJoinColumns = + @JoinColumn(name = "role_id", referencedColumnName = "id")) + private Collection roles; } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/ContainerRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/ContainerRepository.java index e9d19e9..6c0c3bd 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/ContainerRepository.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/ContainerRepository.java @@ -1,11 +1,10 @@ package com.sms.stockmanagementsystem.project.repositories; import com.sms.stockmanagementsystem.project.data.Container; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import java.util.List; - @Repository public interface ContainerRepository extends JpaRepository { diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/GroupRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/GroupRepository.java index d7f220b..f9f9f1e 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/GroupRepository.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/GroupRepository.java @@ -1,9 +1,8 @@ package com.sms.stockmanagementsystem.project.repositories; import com.sms.stockmanagementsystem.project.data.Group; -import org.springframework.data.jpa.repository.JpaRepository; - import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; public interface GroupRepository extends JpaRepository { diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java index 46bc316..b2b92a5 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java @@ -7,5 +7,5 @@ @Repository public interface PrivilegeRepository extends JpaRepository { - Privilege findByName(String name); + Privilege findByName(String name); } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java index fd85dd0..cfd9189 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java @@ -4,9 +4,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; - @Repository public interface RoleRepository extends JpaRepository { - Role findByName(String name); + Role findByName(String name); } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java index c244c7e..bb1589a 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java +++ b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java @@ -1,13 +1,14 @@ package com.sms.stockmanagementsystem.project.repositories.authentication; - import com.sms.stockmanagementsystem.project.data.authentication.User; + +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository { - User findByEmail(String email); - + Optional findByUsername(String username); } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/services/SmsUserDetailsService.java b/src/main/java/com/sms/stockmanagementsystem/project/services/SmsUserDetailsService.java new file mode 100644 index 0000000..52b53d8 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/services/SmsUserDetailsService.java @@ -0,0 +1,78 @@ +package com.sms.stockmanagementsystem.project.services; + +import com.sms.stockmanagementsystem.project.data.authentication.Privilege; +import com.sms.stockmanagementsystem.project.data.authentication.Role; +import com.sms.stockmanagementsystem.project.data.authentication.User; +import com.sms.stockmanagementsystem.project.repositories.authentication.RoleRepository; +import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; +import jakarta.transaction.Transactional; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service("userDetailsService") +@Transactional +public class SmsUserDetailsService implements UserDetailsService { + + @Autowired private UserRepository userRepository; + + // @Autowired + // private IUserService service; + + @Autowired private RoleRepository roleRepository; + + @Override + public UserDetails loadUserByUsername(String username) + throws UsernameNotFoundException { + + User user = userRepository.findByUsername(username).orElseThrow( + () -> new UsernameNotFoundException("User not found")); + if (user == null) { + return new org.springframework.security.core.userdetails.User( + " ", " ", true, true, true, true, + getAuthorities( + Arrays.asList(roleRepository.findByName("ROLE_USER")))); + } + + return new org.springframework.security.core.userdetails.User( + user.getUsername(), user.getPassword(), user.isEnabled(), true, true, + true, getAuthorities(user.getRoles())); + } + + private Collection + getAuthorities(Collection roles) { + + return getGrantedAuthorities(getPrivileges(roles)); + } + + private List getPrivileges(Collection roles) { + + List privileges = new ArrayList<>(); + List collection = new ArrayList<>(); + for (Role role : roles) { + privileges.add(role.getName()); + collection.addAll(role.getPrivileges()); + } + for (Privilege item : collection) { + privileges.add(item.getName()); + } + return privileges; + } + + private List + getGrantedAuthorities(List privileges) { + List authorities = new ArrayList<>(); + for (String privilege : privileges) { + authorities.add(new SimpleGrantedAuthority(privilege)); + } + return authorities; + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/services/UserService.java b/src/main/java/com/sms/stockmanagementsystem/project/services/UserService.java new file mode 100644 index 0000000..8fe2164 --- /dev/null +++ b/src/main/java/com/sms/stockmanagementsystem/project/services/UserService.java @@ -0,0 +1,58 @@ +package com.sms.stockmanagementsystem.project.services; + +import com.sms.stockmanagementsystem.project.data.authentication.User; +import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +/** + * UserService + */ +@Service +public class UserService { + + @Autowired UserRepository userRepository; + + @Autowired PasswordEncoder passwordEncoder; + + /** + * Updates the password for a given user if the current password is correct. + * + * @param username The username of the user. + * @param currentPassword The current password of the user. + * @param newPassword The new password to be set. + * @throws Exception if the current password is incorrect or the user is not + * found. + */ + public void updatePassword(String username, String currentPassword, + String newPassword) throws Exception { + User user = userRepository.findByUsername(username).orElseThrow( + () -> new UsernameNotFoundException("User not found")); + + if (!passwordEncoder.matches(currentPassword, user.getPassword())) { + throw new Exception("Current password is incorrect"); + } + + user.setPassword(passwordEncoder.encode(newPassword)); + userRepository.save(user); + } + + /** + * Checks if a user exists by username. + * + * @param username The username to check. + * @return true if the user exists, false otherwise. + */ + public boolean existsByUsername(String username) { + return userRepository.findByUsername(username).isPresent(); + } + + /** + * Saves a user to the repository. + * + * @param user The user to save. + */ + public void saveUser(User user) { userRepository.save(user); } +} From b0b8d11b88cb5e54215fce26b4d283d33a5ce0f1 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Thu, 27 Feb 2025 12:32:44 +0100 Subject: [PATCH 05/13] --wip-- [skip ci] --- .editorconfig | 1348 +++++++++++++++++ build.gradle | 18 +- dev/docker-compose-dev.yml | 4 +- settings.gradle | 2 +- .../project/SetupDataLoader.java | 88 -- .../authentication/UserRepository.java | 14 - .../GlobalExceptionHandler.java | 22 + .../SecurityConfig.java | 15 +- .../SetupDataLoader.java | 132 ++ .../StockManagementSystemApplication.java | 17 +- .../controllers/v1/ApiKeyController.java | 62 + .../controllers/v1/ContainerController.java | 70 +- .../controllers/v1/GroupController.java | 10 +- .../controllers/v1/PrivilegeController.java | 40 + .../controllers/v1/UserController.java | 37 + .../data/Container.java | 2 +- .../stockmanagementsystem}/data/Group.java | 2 +- .../data/authentication/ApiKey.java | 33 + .../data/authentication/Privilege.java | 2 +- .../data/authentication/Role.java | 2 +- .../data/authentication/User.java | 9 +- .../data/dto/authentication/ApiKeyDto.java | 28 + .../dto/authentication/ApiKeyWithUserDto.java | 34 + .../data/dto/authentication/PrivilegeDto.java | 22 + .../data/dto/authentication/UserDto.java | 26 + .../dto/authentication/UserWithApiKeyDto.java | 28 + .../repositories/ContainerRepository.java | 4 +- .../repositories/GroupRepository.java | 4 +- .../authentication/ApiKeyRepository.java | 17 + .../authentication/PrivilegeRepository.java | 4 +- .../authentication/RoleRepository.java | 4 +- .../authentication/UserRepository.java | 15 + .../services/ApiKeyService.java | 80 + .../services/PrivilegeService.java | 22 + .../services/SmsUserDetailsService.java | 15 +- .../services/UserService.java | 13 +- .../utils/AnnotationScanner.java | 69 + src/main/resources/application.yml | 1 + 38 files changed, 2128 insertions(+), 187 deletions(-) create mode 100644 .editorconfig delete mode 100644 src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java delete mode 100644 src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/GlobalExceptionHandler.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/SecurityConfig.java (72%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/StockManagementSystemApplication.java (76%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/controllers/v1/ContainerController.java (53%) rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/controllers/v1/GroupController.java (93%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/data/Container.java (96%) rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/data/Group.java (95%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/data/authentication/Privilege.java (86%) rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/data/authentication/Role.java (91%) rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/data/authentication/User.java (74%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyDto.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyWithUserDto.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/PrivilegeDto.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/repositories/ContainerRepository.java (74%) rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/repositories/GroupRepository.java (63%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/repositories/authentication/PrivilegeRepository.java (61%) rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/repositories/authentication/RoleRepository.java (61%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/UserRepository.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/services/PrivilegeService.java rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/services/SmsUserDetailsService.java (82%) rename src/main/java/{com/sms/stockmanagementsystem/project => eu/goldenkoopa/stockmanagementsystem}/services/UserService.java (81%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/utils/AnnotationScanner.java diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..de636c5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,1348 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 80 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_wrap_on_typing = false + +[.gitignore] +max_line_length = unset + +[*.css] +ij_css_align_closing_brace_with_properties = false +ij_css_blank_lines_around_nested_selector = 1 +ij_css_blank_lines_between_blocks = 1 +ij_css_brace_placement = 0 +ij_css_hex_color_long_format = false +ij_css_hex_color_lower_case = false +ij_css_hex_color_short_format = false +ij_css_hex_color_upper_case = false +ij_css_keep_blank_lines_in_code = 2 +ij_css_keep_indents_on_empty_lines = false +ij_css_keep_single_line_blocks = false +ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_css_space_after_colon = true +ij_css_space_before_opening_brace = true +ij_css_value_alignment = 0 + +[*.md] +max_line_length = unset + +[*.feature] +tab_width = 4 +ij_continuation_indent_size = 8 +ij_gherkin_keep_indents_on_empty_lines = false + +[*.gsp] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_gsp_keep_indents_on_empty_lines = false + +[*.haml] +tab_width = 4 +ij_continuation_indent_size = 8 +ij_haml_keep_indents_on_empty_lines = false + +[*.java] +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = false +ij_java_align_multiline_array_initializer_expression = false +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = false +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = false +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = false +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_resources = false +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = false +ij_java_annotation_parameter_wrap = off +ij_java_array_initializer_new_line_after_left_brace = false +ij_java_array_initializer_right_brace_on_new_line = false +ij_java_array_initializer_wrap = normal +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = off +ij_java_assignment_wrap = off +ij_java_binary_operation_sign_on_next_line = true +ij_java_binary_operation_wrap = normal +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 1 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_before_imports = 1 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 0 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_at_first_column = true +ij_java_call_parameters_new_line_after_left_paren = false +ij_java_call_parameters_right_paren_on_new_line = false +ij_java_call_parameters_wrap = normal +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_class_names_in_javadoc = 1 +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_while_brace_force = always +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = true +ij_java_doc_align_param_comments = true +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = false +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = false +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_entity_dd_suffix = EJB +ij_java_entity_eb_suffix = Bean +ij_java_entity_hi_suffix = Home +ij_java_entity_lhi_prefix = Local +ij_java_entity_lhi_suffix = Home +ij_java_entity_li_prefix = Local +ij_java_entity_pk_class = java.lang.String +ij_java_entity_vo_suffix = VO +ij_java_enum_constants_wrap = off +ij_java_extends_keyword_wrap = off +ij_java_extends_list_wrap = normal +ij_java_field_annotation_wrap = split_into_lines +ij_java_finally_on_new_line = false +ij_java_for_brace_force = always +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = false +ij_java_for_statement_wrap = normal +ij_java_generate_final_locals = false +ij_java_generate_final_parameters = false +ij_java_if_brace_force = always +ij_java_imports_layout = $*,|,* +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = true +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 1 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_control_statement_in_one_line = false +ij_java_keep_first_column_comment = true +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = true +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = false +ij_java_keep_simple_lambdas_in_one_line = false +ij_java_keep_simple_methods_in_one_line = false +ij_java_lambda_brace_style = end_of_line +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = false +ij_java_line_comment_at_first_column = true +ij_java_message_dd_suffix = EJB +ij_java_message_eb_suffix = Bean +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = normal +ij_java_method_parameters_new_line_after_left_paren = false +ij_java_method_parameters_right_paren_on_new_line = false +ij_java_method_parameters_wrap = normal +ij_java_modifier_list_wrap = false +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_parameter_annotation_wrap = off +ij_java_parentheses_expression_new_line_after_left_paren = false +ij_java_parentheses_expression_right_paren_on_new_line = false +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = false +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = false +ij_java_resource_list_right_paren_on_new_line = false +ij_java_resource_list_wrap = off +ij_java_session_dd_suffix = EJB +ij_java_session_eb_suffix = Bean +ij_java_session_hi_suffix = Home +ij_java_session_lhi_prefix = Local +ij_java_session_lhi_suffix = Home +ij_java_session_li_prefix = Local +ij_java_session_si_suffix = Service +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = false +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = false +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = false +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_subclass_name_suffix = Impl +ij_java_ternary_operation_signs_on_next_line = true +ij_java_ternary_operation_wrap = normal +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = normal +ij_java_throws_list_wrap = off +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = off +ij_java_visibility = public +ij_java_while_brace_force = always +ij_java_while_on_new_line = false +ij_java_wrap_comments = true +ij_java_wrap_first_method_in_call_chain = false +ij_java_wrap_long_lines = false + +[*.less] +tab_width = 4 +ij_continuation_indent_size = 8 +ij_less_align_closing_brace_with_properties = false +ij_less_blank_lines_around_nested_selector = 1 +ij_less_blank_lines_between_blocks = 1 +ij_less_brace_placement = 0 +ij_less_hex_color_long_format = false +ij_less_hex_color_lower_case = false +ij_less_hex_color_short_format = false +ij_less_hex_color_upper_case = false +ij_less_keep_blank_lines_in_code = 2 +ij_less_keep_indents_on_empty_lines = false +ij_less_keep_single_line_blocks = false +ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_less_space_after_colon = true +ij_less_space_before_opening_brace = true +ij_less_value_alignment = 0 + +[*.sass] +ij_sass_align_closing_brace_with_properties = false +ij_sass_blank_lines_around_nested_selector = 1 +ij_sass_blank_lines_between_blocks = 1 +ij_sass_brace_placement = 0 +ij_sass_hex_color_long_format = false +ij_sass_hex_color_lower_case = false +ij_sass_hex_color_short_format = false +ij_sass_hex_color_upper_case = false +ij_sass_keep_blank_lines_in_code = 2 +ij_sass_keep_indents_on_empty_lines = false +ij_sass_keep_single_line_blocks = false +ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_sass_space_after_colon = true +ij_sass_space_before_opening_brace = true +ij_sass_value_alignment = 0 + +[*.scss] +ij_scss_align_closing_brace_with_properties = false +ij_scss_blank_lines_around_nested_selector = 1 +ij_scss_blank_lines_between_blocks = 1 +ij_scss_brace_placement = 0 +ij_scss_hex_color_long_format = false +ij_scss_hex_color_lower_case = false +ij_scss_hex_color_short_format = false +ij_scss_hex_color_upper_case = false +ij_scss_keep_blank_lines_in_code = 2 +ij_scss_keep_indents_on_empty_lines = false +ij_scss_keep_single_line_blocks = false +ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_scss_space_after_colon = true +ij_scss_space_before_opening_brace = true +ij_scss_value_alignment = 0 + +[*.styl] +tab_width = 4 +ij_continuation_indent_size = 8 +ij_stylus_align_closing_brace_with_properties = false +ij_stylus_blank_lines_around_nested_selector = 1 +ij_stylus_blank_lines_between_blocks = 1 +ij_stylus_brace_placement = 0 +ij_stylus_hex_color_long_format = false +ij_stylus_hex_color_lower_case = false +ij_stylus_hex_color_short_format = false +ij_stylus_hex_color_upper_case = false +ij_stylus_keep_blank_lines_in_code = 2 +ij_stylus_keep_indents_on_empty_lines = false +ij_stylus_keep_single_line_blocks = false +ij_stylus_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_stylus_space_after_colon = true +ij_stylus_space_before_opening_brace = true +ij_stylus_value_alignment = 0 + +[.editorconfig] +max_line_length = unset +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.as,*.js2,*.es}] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_actionscript_align_imports = false +ij_actionscript_align_multiline_array_initializer_expression = false +ij_actionscript_align_multiline_binary_operation = false +ij_actionscript_align_multiline_chained_methods = false +ij_actionscript_align_multiline_extends_list = false +ij_actionscript_align_multiline_for = false +ij_actionscript_align_multiline_parameters = false +ij_actionscript_align_multiline_parameters_in_calls = false +ij_actionscript_align_multiline_ternary_operation = false +ij_actionscript_align_object_properties = 0 +ij_actionscript_align_union_types = false +ij_actionscript_align_var_statements = 0 +ij_actionscript_array_initializer_new_line_after_left_brace = false +ij_actionscript_array_initializer_right_brace_on_new_line = false +ij_actionscript_array_initializer_wrap = normal +ij_actionscript_assignment_wrap = off +ij_actionscript_binary_operation_sign_on_next_line = true +ij_actionscript_binary_operation_wrap = normal +ij_actionscript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** +ij_actionscript_blank_lines_after_imports = 1 +ij_actionscript_blank_lines_after_package = 0 +ij_actionscript_blank_lines_around_function = 1 +ij_actionscript_blank_lines_around_method = 1 +ij_actionscript_blank_lines_before_imports = 1 +ij_actionscript_blank_lines_before_package = 0 +ij_actionscript_block_brace_style = end_of_line +ij_actionscript_call_parameters_new_line_after_left_paren = false +ij_actionscript_call_parameters_right_paren_on_new_line = false +ij_actionscript_call_parameters_wrap = normal +ij_actionscript_catch_on_new_line = false +ij_actionscript_chained_call_dot_on_new_line = true +ij_actionscript_class_brace_style = end_of_line +ij_actionscript_comma_on_new_line = false +ij_actionscript_do_while_brace_force = always +ij_actionscript_else_on_new_line = false +ij_actionscript_enforce_trailing_comma = keep +ij_actionscript_extends_keyword_wrap = off +ij_actionscript_extends_list_wrap = normal +ij_actionscript_field_prefix = _ +ij_actionscript_file_name_style = relaxed +ij_actionscript_finally_on_new_line = false +ij_actionscript_for_brace_force = always +ij_actionscript_for_statement_new_line_after_left_paren = false +ij_actionscript_for_statement_right_paren_on_new_line = false +ij_actionscript_for_statement_wrap = normal +ij_actionscript_force_quote_style = false +ij_actionscript_force_semicolon_style = false +ij_actionscript_function_expression_brace_style = end_of_line +ij_actionscript_if_brace_force = always +ij_actionscript_import_merge_members = global +ij_actionscript_import_prefer_absolute_path = global +ij_actionscript_import_sort_members = true +ij_actionscript_import_sort_module_name = false +ij_actionscript_import_use_node_resolution = true +ij_actionscript_imports_wrap = on_every_item +ij_actionscript_indent_case_from_switch = true +ij_actionscript_indent_chained_calls = true +ij_actionscript_indent_package_children = 0 +ij_actionscript_jsx_attribute_value = braces +ij_actionscript_keep_blank_lines_in_code = 1 +ij_actionscript_keep_first_column_comment = true +ij_actionscript_keep_indents_on_empty_lines = false +ij_actionscript_keep_line_breaks = true +ij_actionscript_keep_simple_blocks_in_one_line = false +ij_actionscript_keep_simple_methods_in_one_line = false +ij_actionscript_line_comment_at_first_column = true +ij_actionscript_method_brace_style = end_of_line +ij_actionscript_method_call_chain_wrap = off +ij_actionscript_method_parameters_new_line_after_left_paren = false +ij_actionscript_method_parameters_right_paren_on_new_line = false +ij_actionscript_method_parameters_wrap = normal +ij_actionscript_object_literal_wrap = on_every_item +ij_actionscript_parentheses_expression_new_line_after_left_paren = false +ij_actionscript_parentheses_expression_right_paren_on_new_line = false +ij_actionscript_place_assignment_sign_on_next_line = false +ij_actionscript_prefer_as_type_cast = false +ij_actionscript_prefer_parameters_wrap = false +ij_actionscript_reformat_c_style_comments = false +ij_actionscript_space_after_colon = true +ij_actionscript_space_after_comma = true +ij_actionscript_space_after_dots_in_rest_parameter = false +ij_actionscript_space_after_generator_mult = true +ij_actionscript_space_after_property_colon = true +ij_actionscript_space_after_quest = true +ij_actionscript_space_after_type_colon = false +ij_actionscript_space_after_unary_not = false +ij_actionscript_space_before_async_arrow_lparen = true +ij_actionscript_space_before_catch_keyword = true +ij_actionscript_space_before_catch_left_brace = true +ij_actionscript_space_before_catch_parentheses = true +ij_actionscript_space_before_class_lbrace = true +ij_actionscript_space_before_colon = true +ij_actionscript_space_before_comma = false +ij_actionscript_space_before_do_left_brace = true +ij_actionscript_space_before_else_keyword = true +ij_actionscript_space_before_else_left_brace = true +ij_actionscript_space_before_finally_keyword = true +ij_actionscript_space_before_finally_left_brace = true +ij_actionscript_space_before_for_left_brace = true +ij_actionscript_space_before_for_parentheses = true +ij_actionscript_space_before_for_semicolon = false +ij_actionscript_space_before_function_left_parenth = true +ij_actionscript_space_before_generator_mult = false +ij_actionscript_space_before_if_left_brace = true +ij_actionscript_space_before_if_parentheses = true +ij_actionscript_space_before_method_call_parentheses = false +ij_actionscript_space_before_method_left_brace = true +ij_actionscript_space_before_method_parentheses = false +ij_actionscript_space_before_property_colon = false +ij_actionscript_space_before_quest = true +ij_actionscript_space_before_switch_left_brace = true +ij_actionscript_space_before_switch_parentheses = true +ij_actionscript_space_before_try_left_brace = true +ij_actionscript_space_before_type_colon = false +ij_actionscript_space_before_unary_not = false +ij_actionscript_space_before_while_keyword = true +ij_actionscript_space_before_while_left_brace = true +ij_actionscript_space_before_while_parentheses = true +ij_actionscript_spaces_around_additive_operators = true +ij_actionscript_spaces_around_arrow_function_operator = true +ij_actionscript_spaces_around_assignment_operators = true +ij_actionscript_spaces_around_bitwise_operators = true +ij_actionscript_spaces_around_equality_operators = true +ij_actionscript_spaces_around_logical_operators = true +ij_actionscript_spaces_around_multiplicative_operators = true +ij_actionscript_spaces_around_relational_operators = true +ij_actionscript_spaces_around_shift_operators = true +ij_actionscript_spaces_around_unary_operator = false +ij_actionscript_spaces_within_array_initializer_brackets = false +ij_actionscript_spaces_within_brackets = false +ij_actionscript_spaces_within_catch_parentheses = false +ij_actionscript_spaces_within_for_parentheses = false +ij_actionscript_spaces_within_if_parentheses = false +ij_actionscript_spaces_within_imports = false +ij_actionscript_spaces_within_interpolation_expressions = false +ij_actionscript_spaces_within_method_call_parentheses = false +ij_actionscript_spaces_within_method_parentheses = false +ij_actionscript_spaces_within_object_literal_braces = false +ij_actionscript_spaces_within_object_type_braces = true +ij_actionscript_spaces_within_parentheses = false +ij_actionscript_spaces_within_switch_parentheses = false +ij_actionscript_spaces_within_type_assertion = false +ij_actionscript_spaces_within_union_types = true +ij_actionscript_spaces_within_while_parentheses = false +ij_actionscript_special_else_if_treatment = true +ij_actionscript_ternary_operation_signs_on_next_line = true +ij_actionscript_ternary_operation_wrap = normal +ij_actionscript_union_types_wrap = on_every_item +ij_actionscript_use_chained_calls_group_indents = false +ij_actionscript_use_double_quotes = true +ij_actionscript_use_explicit_js_extension = global +ij_actionscript_use_path_mapping = always +ij_actionscript_use_public_modifier = false +ij_actionscript_use_semicolon_after_statement = true +ij_actionscript_var_declaration_wrap = normal +ij_actionscript_while_brace_force = always +ij_actionscript_while_on_new_line = false +ij_actionscript_wrap_comments = false + +[{*.ats,*.ts}] +ij_typescript_align_imports = false +ij_typescript_align_multiline_array_initializer_expression = false +ij_typescript_align_multiline_binary_operation = false +ij_typescript_align_multiline_chained_methods = false +ij_typescript_align_multiline_extends_list = false +ij_typescript_align_multiline_for = true +ij_typescript_align_multiline_parameters = true +ij_typescript_align_multiline_parameters_in_calls = false +ij_typescript_align_multiline_ternary_operation = false +ij_typescript_align_object_properties = 0 +ij_typescript_align_union_types = false +ij_typescript_align_var_statements = 0 +ij_typescript_array_initializer_new_line_after_left_brace = false +ij_typescript_array_initializer_right_brace_on_new_line = false +ij_typescript_array_initializer_wrap = off +ij_typescript_assignment_wrap = off +ij_typescript_binary_operation_sign_on_next_line = false +ij_typescript_binary_operation_wrap = off +ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** +ij_typescript_blank_lines_after_imports = 1 +ij_typescript_blank_lines_around_class = 1 +ij_typescript_blank_lines_around_field = 0 +ij_typescript_blank_lines_around_field_in_interface = 0 +ij_typescript_blank_lines_around_function = 1 +ij_typescript_blank_lines_around_method = 1 +ij_typescript_blank_lines_around_method_in_interface = 1 +ij_typescript_block_brace_style = end_of_line +ij_typescript_call_parameters_new_line_after_left_paren = false +ij_typescript_call_parameters_right_paren_on_new_line = false +ij_typescript_call_parameters_wrap = off +ij_typescript_catch_on_new_line = false +ij_typescript_chained_call_dot_on_new_line = true +ij_typescript_class_brace_style = end_of_line +ij_typescript_comma_on_new_line = false +ij_typescript_do_while_brace_force = never +ij_typescript_else_on_new_line = false +ij_typescript_enforce_trailing_comma = keep +ij_typescript_extends_keyword_wrap = off +ij_typescript_extends_list_wrap = off +ij_typescript_field_prefix = _ +ij_typescript_file_name_style = relaxed +ij_typescript_finally_on_new_line = false +ij_typescript_for_brace_force = never +ij_typescript_for_statement_new_line_after_left_paren = false +ij_typescript_for_statement_right_paren_on_new_line = false +ij_typescript_for_statement_wrap = off +ij_typescript_force_quote_style = false +ij_typescript_force_semicolon_style = false +ij_typescript_function_expression_brace_style = end_of_line +ij_typescript_if_brace_force = never +ij_typescript_import_merge_members = global +ij_typescript_import_prefer_absolute_path = global +ij_typescript_import_sort_members = true +ij_typescript_import_sort_module_name = false +ij_typescript_import_use_node_resolution = true +ij_typescript_imports_wrap = on_every_item +ij_typescript_indent_case_from_switch = true +ij_typescript_indent_chained_calls = false +ij_typescript_indent_package_children = 0 +ij_typescript_jsdoc_include_types = false +ij_typescript_jsx_attribute_value = braces +ij_typescript_keep_blank_lines_in_code = 2 +ij_typescript_keep_first_column_comment = true +ij_typescript_keep_indents_on_empty_lines = false +ij_typescript_keep_line_breaks = true +ij_typescript_keep_simple_blocks_in_one_line = false +ij_typescript_keep_simple_methods_in_one_line = false +ij_typescript_line_comment_add_space = true +ij_typescript_line_comment_at_first_column = false +ij_typescript_method_brace_style = end_of_line +ij_typescript_method_call_chain_wrap = off +ij_typescript_method_parameters_new_line_after_left_paren = false +ij_typescript_method_parameters_right_paren_on_new_line = false +ij_typescript_method_parameters_wrap = off +ij_typescript_object_literal_wrap = on_every_item +ij_typescript_parentheses_expression_new_line_after_left_paren = false +ij_typescript_parentheses_expression_right_paren_on_new_line = false +ij_typescript_place_assignment_sign_on_next_line = false +ij_typescript_prefer_as_type_cast = false +ij_typescript_prefer_parameters_wrap = false +ij_typescript_reformat_c_style_comments = false +ij_typescript_space_after_colon = true +ij_typescript_space_after_comma = true +ij_typescript_space_after_dots_in_rest_parameter = false +ij_typescript_space_after_generator_mult = true +ij_typescript_space_after_property_colon = true +ij_typescript_space_after_quest = true +ij_typescript_space_after_type_colon = true +ij_typescript_space_after_unary_not = false +ij_typescript_space_before_async_arrow_lparen = true +ij_typescript_space_before_catch_keyword = true +ij_typescript_space_before_catch_left_brace = true +ij_typescript_space_before_catch_parentheses = true +ij_typescript_space_before_class_lbrace = true +ij_typescript_space_before_class_left_brace = true +ij_typescript_space_before_colon = true +ij_typescript_space_before_comma = false +ij_typescript_space_before_do_left_brace = true +ij_typescript_space_before_else_keyword = true +ij_typescript_space_before_else_left_brace = true +ij_typescript_space_before_finally_keyword = true +ij_typescript_space_before_finally_left_brace = true +ij_typescript_space_before_for_left_brace = true +ij_typescript_space_before_for_parentheses = true +ij_typescript_space_before_for_semicolon = false +ij_typescript_space_before_function_left_parenth = true +ij_typescript_space_before_generator_mult = false +ij_typescript_space_before_if_left_brace = true +ij_typescript_space_before_if_parentheses = true +ij_typescript_space_before_method_call_parentheses = false +ij_typescript_space_before_method_left_brace = true +ij_typescript_space_before_method_parentheses = false +ij_typescript_space_before_property_colon = false +ij_typescript_space_before_quest = true +ij_typescript_space_before_switch_left_brace = true +ij_typescript_space_before_switch_parentheses = true +ij_typescript_space_before_try_left_brace = true +ij_typescript_space_before_type_colon = false +ij_typescript_space_before_unary_not = false +ij_typescript_space_before_while_keyword = true +ij_typescript_space_before_while_left_brace = true +ij_typescript_space_before_while_parentheses = true +ij_typescript_spaces_around_additive_operators = true +ij_typescript_spaces_around_arrow_function_operator = true +ij_typescript_spaces_around_assignment_operators = true +ij_typescript_spaces_around_bitwise_operators = true +ij_typescript_spaces_around_equality_operators = true +ij_typescript_spaces_around_logical_operators = true +ij_typescript_spaces_around_multiplicative_operators = true +ij_typescript_spaces_around_relational_operators = true +ij_typescript_spaces_around_shift_operators = true +ij_typescript_spaces_around_unary_operator = false +ij_typescript_spaces_within_array_initializer_brackets = false +ij_typescript_spaces_within_brackets = false +ij_typescript_spaces_within_catch_parentheses = false +ij_typescript_spaces_within_for_parentheses = false +ij_typescript_spaces_within_if_parentheses = false +ij_typescript_spaces_within_imports = false +ij_typescript_spaces_within_interpolation_expressions = false +ij_typescript_spaces_within_method_call_parentheses = false +ij_typescript_spaces_within_method_parentheses = false +ij_typescript_spaces_within_object_literal_braces = false +ij_typescript_spaces_within_object_type_braces = true +ij_typescript_spaces_within_parentheses = false +ij_typescript_spaces_within_switch_parentheses = false +ij_typescript_spaces_within_type_assertion = false +ij_typescript_spaces_within_union_types = true +ij_typescript_spaces_within_while_parentheses = false +ij_typescript_special_else_if_treatment = true +ij_typescript_ternary_operation_signs_on_next_line = false +ij_typescript_ternary_operation_wrap = off +ij_typescript_union_types_wrap = on_every_item +ij_typescript_use_chained_calls_group_indents = false +ij_typescript_use_double_quotes = true +ij_typescript_use_explicit_js_extension = global +ij_typescript_use_path_mapping = always +ij_typescript_use_public_modifier = false +ij_typescript_use_semicolon_after_statement = true +ij_typescript_var_declaration_wrap = normal +ij_typescript_while_brace_force = never +ij_typescript_while_on_new_line = false +ij_typescript_wrap_comments = false + +[{*.cfml,*.cfm,*.cfc}] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_cfml_align_multiline_binary_operation = false +ij_cfml_align_multiline_for = true +ij_cfml_align_multiline_parameters = true +ij_cfml_align_multiline_parameters_in_calls = false +ij_cfml_align_multiline_ternary_operation = false +ij_cfml_assignment_wrap = off +ij_cfml_binary_operation_sign_on_next_line = false +ij_cfml_binary_operation_wrap = off +ij_cfml_block_brace_style = end_of_line +ij_cfml_call_parameters_new_line_after_left_paren = false +ij_cfml_call_parameters_right_paren_on_new_line = false +ij_cfml_call_parameters_wrap = off +ij_cfml_catch_on_new_line = false +ij_cfml_else_on_new_line = false +ij_cfml_for_statement_new_line_after_left_paren = false +ij_cfml_for_statement_right_paren_on_new_line = false +ij_cfml_for_statement_wrap = off +ij_cfml_keep_blank_lines_in_code = 2 +ij_cfml_keep_first_column_comment = true +ij_cfml_keep_indents_on_empty_lines = false +ij_cfml_keep_line_breaks = true +ij_cfml_method_brace_style = next_line +ij_cfml_method_parameters_new_line_after_left_paren = false +ij_cfml_method_parameters_right_paren_on_new_line = false +ij_cfml_method_parameters_wrap = off +ij_cfml_parentheses_expression_new_line_after_left_paren = false +ij_cfml_parentheses_expression_right_paren_on_new_line = false +ij_cfml_place_assignment_sign_on_next_line = false +ij_cfml_space_after_colon = true +ij_cfml_space_after_comma = true +ij_cfml_space_after_for_semicolon = true +ij_cfml_space_after_quest = true +ij_cfml_space_before_catch_keyword = true +ij_cfml_space_before_catch_left_brace = true +ij_cfml_space_before_catch_parentheses = true +ij_cfml_space_before_colon = true +ij_cfml_space_before_comma = false +ij_cfml_space_before_else_keyword = true +ij_cfml_space_before_else_left_brace = true +ij_cfml_space_before_for_left_brace = true +ij_cfml_space_before_for_parentheses = true +ij_cfml_space_before_for_semicolon = false +ij_cfml_space_before_if_left_brace = true +ij_cfml_space_before_if_parentheses = true +ij_cfml_space_before_method_call_parentheses = false +ij_cfml_space_before_method_left_brace = true +ij_cfml_space_before_method_parentheses = false +ij_cfml_space_before_quest = true +ij_cfml_space_before_switch_left_brace = true +ij_cfml_space_before_switch_parentheses = true +ij_cfml_space_before_try_left_brace = true +ij_cfml_space_before_while_keyword = true +ij_cfml_space_before_while_left_brace = true +ij_cfml_space_before_while_parentheses = true +ij_cfml_spaces_around_additive_operators = true +ij_cfml_spaces_around_assignment_operators = true +ij_cfml_spaces_around_equality_operators = true +ij_cfml_spaces_around_logical_operators = true +ij_cfml_spaces_around_multiplicative_operators = true +ij_cfml_spaces_around_relational_operators = true +ij_cfml_spaces_around_unary_operator = false +ij_cfml_spaces_within_catch_parentheses = false +ij_cfml_spaces_within_for_parentheses = false +ij_cfml_spaces_within_if_parentheses = false +ij_cfml_spaces_within_method_call_parentheses = false +ij_cfml_spaces_within_method_parentheses = false +ij_cfml_spaces_within_switch_parentheses = false +ij_cfml_spaces_within_while_parentheses = false +ij_cfml_special_else_if_treatment = false +ij_cfml_ternary_operation_signs_on_next_line = false +ij_cfml_ternary_operation_wrap = off +ij_cfml_while_on_new_line = false + +[{*.cjs,*.js}] +max_line_length = 80 +ij_javascript_align_imports = false +ij_javascript_align_multiline_array_initializer_expression = false +ij_javascript_align_multiline_binary_operation = false +ij_javascript_align_multiline_chained_methods = false +ij_javascript_align_multiline_extends_list = false +ij_javascript_align_multiline_for = false +ij_javascript_align_multiline_parameters = false +ij_javascript_align_multiline_parameters_in_calls = false +ij_javascript_align_multiline_ternary_operation = false +ij_javascript_align_object_properties = 0 +ij_javascript_align_union_types = false +ij_javascript_align_var_statements = 0 +ij_javascript_array_initializer_new_line_after_left_brace = false +ij_javascript_array_initializer_right_brace_on_new_line = false +ij_javascript_array_initializer_wrap = normal +ij_javascript_assignment_wrap = off +ij_javascript_binary_operation_sign_on_next_line = true +ij_javascript_binary_operation_wrap = normal +ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** +ij_javascript_blank_lines_after_imports = 1 +ij_javascript_blank_lines_around_class = 1 +ij_javascript_blank_lines_around_field = 0 +ij_javascript_blank_lines_around_function = 1 +ij_javascript_blank_lines_around_method = 1 +ij_javascript_block_brace_style = end_of_line +ij_javascript_call_parameters_new_line_after_left_paren = false +ij_javascript_call_parameters_right_paren_on_new_line = false +ij_javascript_call_parameters_wrap = normal +ij_javascript_catch_on_new_line = false +ij_javascript_chained_call_dot_on_new_line = true +ij_javascript_class_brace_style = end_of_line +ij_javascript_comma_on_new_line = false +ij_javascript_do_while_brace_force = always +ij_javascript_else_on_new_line = false +ij_javascript_enforce_trailing_comma = keep +ij_javascript_extends_keyword_wrap = off +ij_javascript_extends_list_wrap = off +ij_javascript_field_prefix = _ +ij_javascript_file_name_style = relaxed +ij_javascript_finally_on_new_line = false +ij_javascript_for_brace_force = always +ij_javascript_for_statement_new_line_after_left_paren = false +ij_javascript_for_statement_right_paren_on_new_line = false +ij_javascript_for_statement_wrap = normal +ij_javascript_force_quote_style = false +ij_javascript_force_semicolon_style = false +ij_javascript_function_expression_brace_style = end_of_line +ij_javascript_if_brace_force = always +ij_javascript_import_merge_members = global +ij_javascript_import_prefer_absolute_path = global +ij_javascript_import_sort_members = true +ij_javascript_import_sort_module_name = false +ij_javascript_import_use_node_resolution = true +ij_javascript_imports_wrap = on_every_item +ij_javascript_indent_case_from_switch = true +ij_javascript_indent_chained_calls = false +ij_javascript_indent_package_children = 0 +ij_javascript_jsx_attribute_value = braces +ij_javascript_keep_blank_lines_in_code = 1 +ij_javascript_keep_first_column_comment = true +ij_javascript_keep_indents_on_empty_lines = false +ij_javascript_keep_line_breaks = true +ij_javascript_keep_simple_blocks_in_one_line = false +ij_javascript_keep_simple_methods_in_one_line = false +ij_javascript_line_comment_add_space = true +ij_javascript_line_comment_at_first_column = false +ij_javascript_method_brace_style = end_of_line +ij_javascript_method_call_chain_wrap = off +ij_javascript_method_parameters_new_line_after_left_paren = false +ij_javascript_method_parameters_right_paren_on_new_line = false +ij_javascript_method_parameters_wrap = normal +ij_javascript_object_literal_wrap = on_every_item +ij_javascript_parentheses_expression_new_line_after_left_paren = false +ij_javascript_parentheses_expression_right_paren_on_new_line = false +ij_javascript_place_assignment_sign_on_next_line = false +ij_javascript_prefer_as_type_cast = false +ij_javascript_prefer_parameters_wrap = false +ij_javascript_reformat_c_style_comments = false +ij_javascript_space_after_colon = true +ij_javascript_space_after_comma = true +ij_javascript_space_after_dots_in_rest_parameter = false +ij_javascript_space_after_generator_mult = true +ij_javascript_space_after_property_colon = true +ij_javascript_space_after_quest = true +ij_javascript_space_after_type_colon = true +ij_javascript_space_after_unary_not = false +ij_javascript_space_before_async_arrow_lparen = true +ij_javascript_space_before_catch_keyword = true +ij_javascript_space_before_catch_left_brace = true +ij_javascript_space_before_catch_parentheses = true +ij_javascript_space_before_class_lbrace = true +ij_javascript_space_before_class_left_brace = true +ij_javascript_space_before_colon = true +ij_javascript_space_before_comma = false +ij_javascript_space_before_do_left_brace = true +ij_javascript_space_before_else_keyword = true +ij_javascript_space_before_else_left_brace = true +ij_javascript_space_before_finally_keyword = true +ij_javascript_space_before_finally_left_brace = true +ij_javascript_space_before_for_left_brace = true +ij_javascript_space_before_for_parentheses = true +ij_javascript_space_before_for_semicolon = false +ij_javascript_space_before_function_left_parenth = true +ij_javascript_space_before_generator_mult = false +ij_javascript_space_before_if_left_brace = true +ij_javascript_space_before_if_parentheses = true +ij_javascript_space_before_method_call_parentheses = false +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = false +ij_javascript_space_before_property_colon = false +ij_javascript_space_before_quest = true +ij_javascript_space_before_switch_left_brace = true +ij_javascript_space_before_switch_parentheses = true +ij_javascript_space_before_try_left_brace = true +ij_javascript_space_before_type_colon = false +ij_javascript_space_before_unary_not = false +ij_javascript_space_before_while_keyword = true +ij_javascript_space_before_while_left_brace = true +ij_javascript_space_before_while_parentheses = true +ij_javascript_spaces_around_additive_operators = true +ij_javascript_spaces_around_arrow_function_operator = true +ij_javascript_spaces_around_assignment_operators = true +ij_javascript_spaces_around_bitwise_operators = true +ij_javascript_spaces_around_equality_operators = true +ij_javascript_spaces_around_logical_operators = true +ij_javascript_spaces_around_multiplicative_operators = true +ij_javascript_spaces_around_relational_operators = true +ij_javascript_spaces_around_shift_operators = true +ij_javascript_spaces_around_unary_operator = false +ij_javascript_spaces_within_array_initializer_brackets = false +ij_javascript_spaces_within_brackets = false +ij_javascript_spaces_within_catch_parentheses = false +ij_javascript_spaces_within_for_parentheses = false +ij_javascript_spaces_within_if_parentheses = false +ij_javascript_spaces_within_imports = false +ij_javascript_spaces_within_interpolation_expressions = false +ij_javascript_spaces_within_method_call_parentheses = false +ij_javascript_spaces_within_method_parentheses = false +ij_javascript_spaces_within_object_literal_braces = false +ij_javascript_spaces_within_object_type_braces = true +ij_javascript_spaces_within_parentheses = false +ij_javascript_spaces_within_switch_parentheses = false +ij_javascript_spaces_within_type_assertion = false +ij_javascript_spaces_within_union_types = true +ij_javascript_spaces_within_while_parentheses = false +ij_javascript_special_else_if_treatment = true +ij_javascript_ternary_operation_signs_on_next_line = true +ij_javascript_ternary_operation_wrap = normal +ij_javascript_union_types_wrap = on_every_item +ij_javascript_use_chained_calls_group_indents = false +ij_javascript_use_double_quotes = true +ij_javascript_use_explicit_js_extension = global +ij_javascript_use_path_mapping = always +ij_javascript_use_public_modifier = false +ij_javascript_use_semicolon_after_statement = true +ij_javascript_var_declaration_wrap = normal +ij_javascript_while_brace_force = always +ij_javascript_while_on_new_line = false +ij_javascript_wrap_comments = false + +[{*.cjsx,*.coffee}] +ij_continuation_indent_size = 2 +ij_coffeescript_align_function_body = false +ij_coffeescript_align_imports = false +ij_coffeescript_align_multiline_array_initializer_expression = true +ij_coffeescript_align_multiline_parameters = true +ij_coffeescript_align_multiline_parameters_in_calls = false +ij_coffeescript_align_object_properties = 0 +ij_coffeescript_align_union_types = false +ij_coffeescript_align_var_statements = 0 +ij_coffeescript_array_initializer_new_line_after_left_brace = false +ij_coffeescript_array_initializer_right_brace_on_new_line = false +ij_coffeescript_array_initializer_wrap = normal +ij_coffeescript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** +ij_coffeescript_blank_lines_around_function = 1 +ij_coffeescript_call_parameters_new_line_after_left_paren = false +ij_coffeescript_call_parameters_right_paren_on_new_line = false +ij_coffeescript_call_parameters_wrap = normal +ij_coffeescript_chained_call_dot_on_new_line = true +ij_coffeescript_comma_on_new_line = false +ij_coffeescript_enforce_trailing_comma = keep +ij_coffeescript_field_prefix = _ +ij_coffeescript_file_name_style = relaxed +ij_coffeescript_force_quote_style = false +ij_coffeescript_force_semicolon_style = false +ij_coffeescript_function_expression_brace_style = end_of_line +ij_coffeescript_import_merge_members = global +ij_coffeescript_import_prefer_absolute_path = global +ij_coffeescript_import_sort_members = true +ij_coffeescript_import_sort_module_name = false +ij_coffeescript_import_use_node_resolution = true +ij_coffeescript_imports_wrap = on_every_item +ij_coffeescript_indent_chained_calls = true +ij_coffeescript_indent_package_children = 0 +ij_coffeescript_jsx_attribute_value = braces +ij_coffeescript_keep_blank_lines_in_code = 2 +ij_coffeescript_keep_first_column_comment = true +ij_coffeescript_keep_indents_on_empty_lines = false +ij_coffeescript_keep_line_breaks = true +ij_coffeescript_keep_simple_methods_in_one_line = false +ij_coffeescript_method_parameters_new_line_after_left_paren = false +ij_coffeescript_method_parameters_right_paren_on_new_line = false +ij_coffeescript_method_parameters_wrap = off +ij_coffeescript_object_literal_wrap = on_every_item +ij_coffeescript_prefer_as_type_cast = false +ij_coffeescript_reformat_c_style_comments = false +ij_coffeescript_space_after_comma = true +ij_coffeescript_space_after_dots_in_rest_parameter = false +ij_coffeescript_space_after_generator_mult = true +ij_coffeescript_space_after_property_colon = true +ij_coffeescript_space_after_type_colon = true +ij_coffeescript_space_after_unary_not = false +ij_coffeescript_space_before_async_arrow_lparen = true +ij_coffeescript_space_before_class_lbrace = true +ij_coffeescript_space_before_comma = false +ij_coffeescript_space_before_function_left_parenth = true +ij_coffeescript_space_before_generator_mult = false +ij_coffeescript_space_before_property_colon = false +ij_coffeescript_space_before_type_colon = false +ij_coffeescript_space_before_unary_not = false +ij_coffeescript_spaces_around_additive_operators = true +ij_coffeescript_spaces_around_arrow_function_operator = true +ij_coffeescript_spaces_around_assignment_operators = true +ij_coffeescript_spaces_around_bitwise_operators = true +ij_coffeescript_spaces_around_equality_operators = true +ij_coffeescript_spaces_around_logical_operators = true +ij_coffeescript_spaces_around_multiplicative_operators = true +ij_coffeescript_spaces_around_relational_operators = true +ij_coffeescript_spaces_around_shift_operators = true +ij_coffeescript_spaces_around_unary_operator = false +ij_coffeescript_spaces_within_array_initializer_braces = false +ij_coffeescript_spaces_within_array_initializer_brackets = false +ij_coffeescript_spaces_within_imports = false +ij_coffeescript_spaces_within_index_brackets = false +ij_coffeescript_spaces_within_interpolation_expressions = false +ij_coffeescript_spaces_within_method_call_parentheses = false +ij_coffeescript_spaces_within_method_parentheses = false +ij_coffeescript_spaces_within_object_braces = false +ij_coffeescript_spaces_within_object_literal_braces = false +ij_coffeescript_spaces_within_object_type_braces = true +ij_coffeescript_spaces_within_range_brackets = false +ij_coffeescript_spaces_within_type_assertion = false +ij_coffeescript_spaces_within_union_types = true +ij_coffeescript_union_types_wrap = on_every_item +ij_coffeescript_use_chained_calls_group_indents = false +ij_coffeescript_use_double_quotes = true +ij_coffeescript_use_explicit_js_extension = global +ij_coffeescript_use_path_mapping = always +ij_coffeescript_use_public_modifier = false +ij_coffeescript_use_semicolon_after_statement = false +ij_coffeescript_var_declaration_wrap = normal + +[{*.gradle,*.groovy,*.gant,*.gdsl,*.gy,*.gson}] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_groovy_align_group_field_declarations = false +ij_groovy_align_multiline_array_initializer_expression = false +ij_groovy_align_multiline_assignment = false +ij_groovy_align_multiline_binary_operation = false +ij_groovy_align_multiline_chained_methods = false +ij_groovy_align_multiline_extends_list = false +ij_groovy_align_multiline_for = true +ij_groovy_align_multiline_method_parentheses = false +ij_groovy_align_multiline_parameters = true +ij_groovy_align_multiline_parameters_in_calls = false +ij_groovy_align_multiline_resources = true +ij_groovy_align_multiline_ternary_operation = false +ij_groovy_align_multiline_throws_list = false +ij_groovy_align_throws_keyword = false +ij_groovy_array_initializer_new_line_after_left_brace = false +ij_groovy_array_initializer_right_brace_on_new_line = false +ij_groovy_array_initializer_wrap = off +ij_groovy_assert_statement_wrap = off +ij_groovy_assignment_wrap = off +ij_groovy_binary_operation_wrap = off +ij_groovy_blank_lines_after_class_header = 0 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_block_brace_style = end_of_line +ij_groovy_block_comment_at_first_column = true +ij_groovy_call_parameters_new_line_after_left_paren = false +ij_groovy_call_parameters_right_paren_on_new_line = false +ij_groovy_call_parameters_wrap = off +ij_groovy_catch_on_new_line = false +ij_groovy_class_annotation_wrap = split_into_lines +ij_groovy_class_brace_style = end_of_line +ij_groovy_do_while_brace_force = never +ij_groovy_else_on_new_line = false +ij_groovy_enum_constants_wrap = off +ij_groovy_extends_keyword_wrap = off +ij_groovy_extends_list_wrap = off +ij_groovy_field_annotation_wrap = split_into_lines +ij_groovy_finally_on_new_line = false +ij_groovy_for_brace_force = never +ij_groovy_for_statement_new_line_after_left_paren = false +ij_groovy_for_statement_right_paren_on_new_line = false +ij_groovy_for_statement_wrap = off +ij_groovy_if_brace_force = never +ij_groovy_indent_case_from_switch = true +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 2 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = true +ij_groovy_keep_first_column_comment = true +ij_groovy_keep_indents_on_empty_lines = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_lambda_brace_style = end_of_line +ij_groovy_line_comment_add_space = false +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_annotation_wrap = split_into_lines +ij_groovy_method_brace_style = end_of_line +ij_groovy_method_call_chain_wrap = off +ij_groovy_method_parameters_new_line_after_left_paren = false +ij_groovy_method_parameters_right_paren_on_new_line = false +ij_groovy_method_parameters_wrap = off +ij_groovy_modifier_list_wrap = false +ij_groovy_parameter_annotation_wrap = off +ij_groovy_parentheses_expression_new_line_after_left_paren = false +ij_groovy_parentheses_expression_right_paren_on_new_line = false +ij_groovy_prefer_parameters_wrap = false +ij_groovy_resource_list_new_line_after_left_paren = false +ij_groovy_resource_list_right_paren_on_new_line = false +ij_groovy_resource_list_wrap = off +ij_groovy_space_after_colon = true +ij_groovy_space_after_comma = true +ij_groovy_space_after_comma_in_type_arguments = true +ij_groovy_space_after_for_semicolon = true +ij_groovy_space_after_quest = true +ij_groovy_space_after_type_cast = true +ij_groovy_space_before_annotation_parameter_list = false +ij_groovy_space_before_array_initializer_left_brace = false +ij_groovy_space_before_catch_keyword = true +ij_groovy_space_before_catch_left_brace = true +ij_groovy_space_before_catch_parentheses = true +ij_groovy_space_before_class_left_brace = true +ij_groovy_space_before_colon = true +ij_groovy_space_before_comma = false +ij_groovy_space_before_do_left_brace = true +ij_groovy_space_before_else_keyword = true +ij_groovy_space_before_else_left_brace = true +ij_groovy_space_before_finally_keyword = true +ij_groovy_space_before_finally_left_brace = true +ij_groovy_space_before_for_left_brace = true +ij_groovy_space_before_for_parentheses = true +ij_groovy_space_before_for_semicolon = false +ij_groovy_space_before_if_left_brace = true +ij_groovy_space_before_if_parentheses = true +ij_groovy_space_before_method_call_parentheses = false +ij_groovy_space_before_method_left_brace = true +ij_groovy_space_before_method_parentheses = false +ij_groovy_space_before_quest = true +ij_groovy_space_before_switch_left_brace = true +ij_groovy_space_before_switch_parentheses = true +ij_groovy_space_before_synchronized_left_brace = true +ij_groovy_space_before_synchronized_parentheses = true +ij_groovy_space_before_try_left_brace = true +ij_groovy_space_before_try_parentheses = true +ij_groovy_space_before_while_keyword = true +ij_groovy_space_before_while_left_brace = true +ij_groovy_space_before_while_parentheses = true +ij_groovy_space_within_empty_array_initializer_braces = false +ij_groovy_space_within_empty_method_call_parentheses = false +ij_groovy_spaces_around_additive_operators = true +ij_groovy_spaces_around_assignment_operators = true +ij_groovy_spaces_around_bitwise_operators = true +ij_groovy_spaces_around_equality_operators = true +ij_groovy_spaces_around_lambda_arrow = true +ij_groovy_spaces_around_logical_operators = true +ij_groovy_spaces_around_multiplicative_operators = true +ij_groovy_spaces_around_relational_operators = true +ij_groovy_spaces_around_shift_operators = true +ij_groovy_spaces_within_annotation_parentheses = false +ij_groovy_spaces_within_array_initializer_braces = false +ij_groovy_spaces_within_braces = true +ij_groovy_spaces_within_brackets = false +ij_groovy_spaces_within_cast_parentheses = false +ij_groovy_spaces_within_catch_parentheses = false +ij_groovy_spaces_within_for_parentheses = false +ij_groovy_spaces_within_if_parentheses = false +ij_groovy_spaces_within_method_call_parentheses = false +ij_groovy_spaces_within_method_parentheses = false +ij_groovy_spaces_within_parentheses = false +ij_groovy_spaces_within_switch_parentheses = false +ij_groovy_spaces_within_synchronized_parentheses = false +ij_groovy_spaces_within_try_parentheses = false +ij_groovy_spaces_within_while_parentheses = false +ij_groovy_special_else_if_treatment = true +ij_groovy_ternary_operation_wrap = off +ij_groovy_throws_keyword_wrap = off +ij_groovy_throws_list_wrap = off +ij_groovy_variable_annotation_wrap = off +ij_groovy_while_brace_force = never +ij_groovy_while_on_new_line = false +ij_groovy_wrap_long_lines = false + +[{*.jhm,*.xjb,*.rng,*.wsdd,*.wsdl,*.fxml,*.bpmn,*.pom,*.xslt,*.jrxml,*.ant,*.xul,*.xsl,*.xsd,*.tld,*.jnlp,*.wadl,*.xml}] +ij_continuation_indent_size = 2 +ij_xml_block_comment_at_first_column = true +ij_xml_keep_indents_on_empty_lines = false +ij_xml_line_comment_at_first_column = true + +[{*.jspx,*.tagx}] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_jspx_keep_indents_on_empty_lines = false + +[{*.kts,*.kt}] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_kotlin_align_in_columns_case_branch = false +ij_kotlin_align_multiline_binary_operation = false +ij_kotlin_align_multiline_extends_list = false +ij_kotlin_align_multiline_method_parentheses = false +ij_kotlin_align_multiline_parameters = true +ij_kotlin_align_multiline_parameters_in_calls = false +ij_kotlin_assignment_wrap = off +ij_kotlin_blank_lines_after_class_header = 0 +ij_kotlin_blank_lines_around_block_when_branches = 0 +ij_kotlin_block_comment_at_first_column = true +ij_kotlin_call_parameters_new_line_after_left_paren = false +ij_kotlin_call_parameters_right_paren_on_new_line = false +ij_kotlin_call_parameters_wrap = off +ij_kotlin_catch_on_new_line = false +ij_kotlin_class_annotation_wrap = split_into_lines +ij_kotlin_continuation_indent_for_chained_calls = true +ij_kotlin_continuation_indent_for_expression_bodies = true +ij_kotlin_continuation_indent_in_argument_lists = true +ij_kotlin_continuation_indent_in_elvis = true +ij_kotlin_continuation_indent_in_if_conditions = true +ij_kotlin_continuation_indent_in_parameter_lists = true +ij_kotlin_continuation_indent_in_supertype_lists = true +ij_kotlin_else_on_new_line = false +ij_kotlin_enum_constants_wrap = off +ij_kotlin_extends_list_wrap = off +ij_kotlin_field_annotation_wrap = split_into_lines +ij_kotlin_finally_on_new_line = false +ij_kotlin_if_rparen_on_new_line = false +ij_kotlin_import_nested_classes = false +ij_kotlin_insert_whitespaces_in_simple_one_line_method = true +ij_kotlin_keep_blank_lines_before_right_brace = 2 +ij_kotlin_keep_blank_lines_in_code = 2 +ij_kotlin_keep_blank_lines_in_declarations = 2 +ij_kotlin_keep_first_column_comment = true +ij_kotlin_keep_indents_on_empty_lines = false +ij_kotlin_keep_line_breaks = true +ij_kotlin_lbrace_on_next_line = false +ij_kotlin_line_comment_add_space = false +ij_kotlin_line_comment_at_first_column = true +ij_kotlin_method_annotation_wrap = split_into_lines +ij_kotlin_method_call_chain_wrap = off +ij_kotlin_method_parameters_new_line_after_left_paren = false +ij_kotlin_method_parameters_right_paren_on_new_line = false +ij_kotlin_method_parameters_wrap = off +ij_kotlin_name_count_to_use_star_import = 5 +ij_kotlin_name_count_to_use_star_import_for_members = 3 +ij_kotlin_parameter_annotation_wrap = off +ij_kotlin_space_after_comma = true +ij_kotlin_space_after_extend_colon = true +ij_kotlin_space_after_type_colon = true +ij_kotlin_space_before_catch_parentheses = true +ij_kotlin_space_before_comma = false +ij_kotlin_space_before_extend_colon = true +ij_kotlin_space_before_for_parentheses = true +ij_kotlin_space_before_if_parentheses = true +ij_kotlin_space_before_lambda_arrow = true +ij_kotlin_space_before_type_colon = false +ij_kotlin_space_before_when_parentheses = true +ij_kotlin_space_before_while_parentheses = true +ij_kotlin_spaces_around_additive_operators = true +ij_kotlin_spaces_around_assignment_operators = true +ij_kotlin_spaces_around_equality_operators = true +ij_kotlin_spaces_around_function_type_arrow = true +ij_kotlin_spaces_around_logical_operators = true +ij_kotlin_spaces_around_multiplicative_operators = true +ij_kotlin_spaces_around_range = false +ij_kotlin_spaces_around_relational_operators = true +ij_kotlin_spaces_around_unary_operator = false +ij_kotlin_spaces_around_when_arrow = true +ij_kotlin_variable_annotation_wrap = off +ij_kotlin_while_on_new_line = false +ij_kotlin_wrap_elvis_expressions = 1 +ij_kotlin_wrap_expression_body_functions = 0 +ij_kotlin_wrap_first_method_in_call_chain = false + +[{*.sht,*.htm,*.html,*.shtm,*.shtml,*.ng}] +ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p +ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span,pre,textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.vsl,*.vm,*.ft}] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_vtl_keep_indents_on_empty_lines = false + +[{*.xjsp,*.tag,*.jsf,*.jsp,*.jspf,*.tagf}] +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_jsp_jsp_prefer_comma_separated_import_list = false +ij_jsp_keep_indents_on_empty_lines = false + +[{*.yml,*.yaml}] +tab_width = 4 +ij_continuation_indent_size = 2 +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true + +[{*.zsh,*.bash,*.sh}] +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false + +[{.eslintrc,.babelrc,jest.config,.stylelintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}] +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = true +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{messages.*,spring.schemas,spring.handlers,messages,*.properties}] +ij_properties_align_group_field_declarations = false + diff --git a/build.gradle b/build.gradle index 1d1c867..844ad63 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,12 @@ plugins { id 'java' + id 'eclipse' id 'org.springframework.boot' version '3.3.1' id 'io.spring.dependency-management' version '1.1.5' + id 'com.diffplug.eclipse.apt' version '3.26.0' } -group = 'com.sms.stockmanagementsystem' +group = 'eu.goldenkoopa' java { toolchain { @@ -28,7 +30,6 @@ dependencies { testImplementation 'org.springframework.security:spring-security-test' compileOnly 'org.projectlombok:lombok' runtimeOnly 'org.postgresql:postgresql' - annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' implementation 'com.googlecode.json-simple:json-simple:1.1.1' @@ -36,9 +37,21 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.3.1' implementation 'me.paulschwarz:spring-dotenv:4.0.0' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' + // implementation 'org.mapstruct:mapstruct:1.6.3' + // implementation 'org.projectlombok:lombok-mapstruct-binding:0.2.0' + // annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3' + annotationProcessor 'org.projectlombok:lombok' + // implementation 'org.flywaydb:flyway-core:11.3.3' + // implementation 'org.flywaydb:flyway-database-postgresql' + + // testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3' } +bootRun { + environment 'spring.output.ansi.console-available', true +} + tasks.register('buildDockerImage') { group = 'Docker' description = 'Builds docker image and pushes it' @@ -62,3 +75,4 @@ tasks.register('buildDockerImage') { tasks.named('test') { useJUnitPlatform() } + diff --git a/dev/docker-compose-dev.yml b/dev/docker-compose-dev.yml index 5ed43d4..f33b01b 100644 --- a/dev/docker-compose-dev.yml +++ b/dev/docker-compose-dev.yml @@ -2,7 +2,7 @@ version: '3.9' services: postgres: - image: postgres:14-alpine + image: postgres:17-alpine ports: - 5432:5432 volumes: @@ -10,4 +10,4 @@ services: environment: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_DB=${POSTGRES_DB} \ No newline at end of file + - POSTGRES_DB=${POSTGRES_DB} diff --git a/settings.gradle b/settings.gradle index 682509e..43928fb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -rootProject.name = 'project' +rootProject.name = 'stockmanagementsystem' diff --git a/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java b/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java deleted file mode 100644 index bd35efc..0000000 --- a/src/main/java/com/sms/stockmanagementsystem/project/SetupDataLoader.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.sms.stockmanagementsystem.project; - -import com.sms.stockmanagementsystem.project.data.authentication.Privilege; -import com.sms.stockmanagementsystem.project.data.authentication.Role; -import com.sms.stockmanagementsystem.project.data.authentication.User; -import com.sms.stockmanagementsystem.project.repositories.authentication.PrivilegeRepository; -import com.sms.stockmanagementsystem.project.repositories.authentication.RoleRepository; -import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; -import jakarta.transaction.Transactional; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Component; - -@Component -public class SetupDataLoader - implements ApplicationListener { - - boolean alreadySetup = false; - - @Autowired private UserRepository userRepository; - - @Autowired private RoleRepository roleRepository; - - @Autowired private PrivilegeRepository privilegeRepository; - - @Autowired PasswordEncoder passwordEncoder; - - // @Autowired - // private PasswordEncoder passwordEncoder; - - @Override - @Transactional - public void onApplicationEvent(ContextRefreshedEvent event) { - - Privilege readPrivilege = createPrivilegeIfNotFound("READ_PRIVILEGE"); - Privilege writePrivilege = createPrivilegeIfNotFound("WRITE_PRIVILEGE"); - - List adminPrivileges = - Arrays.asList(readPrivilege, writePrivilege); - createRoleIfNotFound("ROLE_ADMIN", adminPrivileges); - createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege)); - - if (userRepository.findByUsername("user").isPresent()) { - return; - } - - Role adminRole = roleRepository.findByName("ROLE_USER"); - User user = new User(); - user.setFirstName("Test"); - user.setLastName("Test"); - user.setPassword(passwordEncoder.encode("test")); - user.setUsername("user"); - user.setRoles(Arrays.asList(adminRole)); - user.setEnabled(true); - userRepository.save(user); - - alreadySetup = true; - } - - @Transactional - Privilege createPrivilegeIfNotFound(String name) { - - Privilege privilege = privilegeRepository.findByName(name); - if (privilege == null) { - privilege = new Privilege(name); - privilegeRepository.save(privilege); - } - return privilege; - } - - @Transactional - Role createRoleIfNotFound(String name, Collection privileges) { - - Role role = roleRepository.findByName(name); - if (role == null) { - role = new Role(name); - role.setPrivileges(privileges); - roleRepository.save(role); - } - return role; - } -} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java b/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java deleted file mode 100644 index bb1589a..0000000 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/UserRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sms.stockmanagementsystem.project.repositories.authentication; - -import com.sms.stockmanagementsystem.project.data.authentication.User; - -import java.util.Optional; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface UserRepository extends JpaRepository { - - Optional findByUsername(String username); -} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/GlobalExceptionHandler.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/GlobalExceptionHandler.java new file mode 100644 index 0000000..6f8e395 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/GlobalExceptionHandler.java @@ -0,0 +1,22 @@ +package eu.goldenkoopa.stockmanagementsystem; + +import java.util.stream.Collectors; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) { + String errorMessage = ex.getBindingResult().getAllErrors().stream() + .map(ObjectError::getDefaultMessage) + .collect(Collectors.joining(", ")); + return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST); + } +} + diff --git a/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SecurityConfig.java similarity index 72% rename from src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/SecurityConfig.java index 916fcbe..174af31 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/SecurityConfig.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SecurityConfig.java @@ -1,33 +1,36 @@ -package com.sms.stockmanagementsystem.project; +package eu.goldenkoopa.stockmanagementsystem; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @Configuration @EnableWebSecurity +@EnableMethodSecurity(securedEnabled = true) public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorize) -> { + // authorize.requestMatchers("/**").permitAll(); authorize.requestMatchers("/").permitAll(); authorize.requestMatchers("/error").permitAll(); authorize.anyRequest().authenticated(); }) .formLogin(Customizer.withDefaults()) .logout(logout -> logout.permitAll()) - .csrf(csrf - -> csrf.csrfTokenRepository( - CookieCsrfTokenRepository.withHttpOnlyFalse())); - ; + .csrf((csrf) -> csrf.ignoringRequestMatchers("/api/**")) + // .csrf(csrf + // -> csrf.csrfTokenRepository( + // CookieCsrfTokenRepository.withHttpOnlyFalse())); + ; return http.build(); } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java new file mode 100644 index 0000000..f887851 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java @@ -0,0 +1,132 @@ +package eu.goldenkoopa.stockmanagementsystem; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Privilege; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Role; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.PrivilegeRepository; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.RoleRepository; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; +import eu.goldenkoopa.stockmanagementsystem.utils.AnnotationScanner; +import jakarta.transaction.Transactional; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +@Component +public class SetupDataLoader + implements ApplicationListener { + + private UserRepository userRepository; + + private RoleRepository roleRepository; + + private PrivilegeRepository privilegeRepository; + + PasswordEncoder passwordEncoder; + + boolean alreadySetup = false; + + @Override + @Transactional + public void onApplicationEvent(ContextRefreshedEvent event) { + + if (alreadySetup) { + return; + } + + try { + List privilegeNames = AnnotationScanner.scan(); + privilegeNames.stream() + .filter(privilegeName -> !privilegeName.startsWith("ROLE_")) + .forEach(this::createPrivilegeIfNotFound); + } catch (Exception e) { + System.err.println("failed to load annotations/privileges" + + e.getMessage()); + e.printStackTrace(); + } + + Privilege readPrivilege = createPrivilegeIfNotFound("READ_PRIVILEGE"); + Privilege writePrivilege = createPrivilegeIfNotFound("WRITE_PRIVILEGE"); + + List adminPrivileges = + Arrays.asList(readPrivilege, writePrivilege); + createRoleIfNotFound("ROLE_ADMIN", adminPrivileges); + createRoleIfNotFound("ROLE_STAFF", Arrays.asList(readPrivilege)); + createRoleIfNotFound("ROLE_TEST", Arrays.asList()); + createRoleIfNotFound("ROLE_USER", Arrays.asList(readPrivilege)); + + // TODO: change!!!! + createAdminIfNotFound(); + createUserIfNotFound(); + alreadySetup = true; + } + + private void createAdminIfNotFound() { + if (userRepository.findByUsername("admin").isPresent()) { + return; + } + Role adminRole = roleRepository.findByName("ROLE_ADMIN"); + User user = new User(); + user.setFirstName("Test"); + user.setLastName("Test"); + user.setPassword(passwordEncoder.encode("admin")); + user.setUsername("admin"); + user.setRoles(Arrays.asList(adminRole)); + user.setEnabled(true); + userRepository.save(user); + } + + private void createUserIfNotFound() { + if (userRepository.findByUsername("user").isPresent()) { + return; + } + Role adminRole = roleRepository.findByName("ROLE_USER"); + User user = new User(); + user.setFirstName("Test"); + user.setLastName("Test"); + user.setPassword(passwordEncoder.encode("test")); + user.setUsername("user"); + user.setRoles(Arrays.asList(adminRole)); + user.setEnabled(true); + userRepository.save(user); + } + + @Transactional + Privilege createPrivilegeIfNotFound(String name) { + + Privilege privilege = privilegeRepository.findByName(name); + if (privilege == null) { + privilege = new Privilege(name); + privilegeRepository.save(privilege); + } + return privilege; + } + + @Transactional + Role createRoleIfNotFound(String name, Collection privileges) { + + Role role = roleRepository.findByName(name); + if (role == null) { + role = new Role(name); + role.setPrivileges(privileges); + roleRepository.save(role); + } + return role; + } + + @Autowired + public SetupDataLoader(UserRepository userRepository, + RoleRepository roleRepository, + PrivilegeRepository privilegeRepository, + PasswordEncoder passwordEncoder) { + this.userRepository = userRepository; + this.roleRepository = roleRepository; + this.privilegeRepository = privilegeRepository; + this.passwordEncoder = passwordEncoder; + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/StockManagementSystemApplication.java similarity index 76% rename from src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/StockManagementSystemApplication.java index 13d64f2..68ae4fe 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/StockManagementSystemApplication.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/StockManagementSystemApplication.java @@ -1,4 +1,4 @@ -package com.sms.stockmanagementsystem.project; +package eu.goldenkoopa.stockmanagementsystem; import org.jetbrains.annotations.NotNull; import org.springframework.boot.CommandLineRunner; @@ -18,7 +18,8 @@ public static void main(String[] args) { SpringApplication.run(StockManagementSystemApplication.class, args); } - @Override public void run(String... args) { // NOSONAR + @Override + public void run(String... args) { // NOSONAR } @Bean @@ -33,17 +34,15 @@ public void addCorsMappings(@NotNull CorsRegistry registry) { @Bean public RoleHierarchy roleHierarchy() { - String hierarchy = "ROLE_ADMIN > ROLE_STAFF \n ROLE_STAFF > ROLE_USER"; - RoleHierarchyImpl roleHierarchy = - RoleHierarchyImpl.fromHierarchy(hierarchy); + String hierarchy = "ROLE_ADMIN > ROLE_STAFF \n ROLE_STAFF > ROLE_TEST \n " + + "ROLE_TEST > ROLE_USER"; + RoleHierarchyImpl roleHierarchy = RoleHierarchyImpl.fromHierarchy(hierarchy); return roleHierarchy; } @Bean - public DefaultWebSecurityExpressionHandler - customWebSecurityExpressionHandler() { - DefaultWebSecurityExpressionHandler expressionHandler = - new DefaultWebSecurityExpressionHandler(); + public DefaultWebSecurityExpressionHandler customWebSecurityExpressionHandler() { + DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler(); expressionHandler.setRoleHierarchy(roleHierarchy()); return expressionHandler; } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java new file mode 100644 index 0000000..6b35e7a --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java @@ -0,0 +1,62 @@ +package eu.goldenkoopa.stockmanagementsystem.controllers.v1; + +import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.ApiKeyWithUserDto; +import eu.goldenkoopa.stockmanagementsystem.services.ApiKeyService; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * ApiKeyController + */ +@RestController +@RequestMapping("/api/v1/apikeys") +@Secured({"ROLE_ADMIN"}) +public class ApiKeyController { + + private final ApiKeyService apiKeyService; + + @GetMapping + public ResponseEntity> getAllApiKeys() { + List apiKeys = apiKeyService.getAllApiKeys() + .stream() + .map(ApiKeyWithUserDto::from) + .toList(); + return new ResponseEntity<>(apiKeys, HttpStatus.OK); + } + + @CrossOrigin + @PostMapping + public ResponseEntity + generateNewApiKey(@RequestBody ApiKeyRequest apiKeyRequest, + @AuthenticationPrincipal UserDetails userDetails) { + ApiKeyWithUserDto apiKey = + ApiKeyWithUserDto.from(apiKeyService.generateNewApiKey( + apiKeyRequest.privilegeIds(), userDetails)); + return new ResponseEntity<>(apiKey, HttpStatus.CREATED); + } + + // private record ApiKeyRequest(List privilegeIds) {} + private record ApiKeyRequest( + @NotNull(message = "Privilege IDs cannot be null test") + @NotEmpty(message = "Privilege IDs cannot be empty test") + List<@NotNull(message = "Privilege ID cannot be null test") Long> + privilegeIds) {} + + @Autowired + public ApiKeyController(ApiKeyService apiKeyService) { + this.apiKeyService = apiKeyService; + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/ContainerController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java similarity index 53% rename from src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/ContainerController.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java index 76c3e38..d9aff60 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/ContainerController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java @@ -1,9 +1,9 @@ -package com.sms.stockmanagementsystem.project.controllers.v1; +package eu.goldenkoopa.stockmanagementsystem.controllers.v1; -import com.sms.stockmanagementsystem.project.data.Container; -import com.sms.stockmanagementsystem.project.data.Group; -import com.sms.stockmanagementsystem.project.repositories.ContainerRepository; -import com.sms.stockmanagementsystem.project.repositories.GroupRepository; +import eu.goldenkoopa.stockmanagementsystem.data.Container; +import eu.goldenkoopa.stockmanagementsystem.data.Group; +import eu.goldenkoopa.stockmanagementsystem.repositories.ContainerRepository; +import eu.goldenkoopa.stockmanagementsystem.repositories.GroupRepository; import java.time.LocalDateTime; import java.util.List; import org.jetbrains.annotations.NotNull; @@ -14,7 +14,14 @@ import org.springframework.http.HttpStatus; import org.springframework.security.access.annotation.Secured; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.HttpServerErrorException; @RestController @@ -26,29 +33,28 @@ public class ContainerController { private final GroupRepository groupRepository; - @Secured({"WRITE_PRIVILEGE"}) + @Secured({ "WRITE_PRIVILEGE" }) @GetMapping("/health") public String health() { return "alive"; } @CrossOrigin + @Secured({"WRITE_PRIVILEGE", "API_CONTAINER_CREATE"}) @PostMapping("/container") - public Container - setContainerDetails(@RequestBody String data, - @NotNull @RequestParam("server") String serverName) + public Container setContainerDetails(@RequestBody String data, + @NotNull @RequestParam("server") String serverName) throws ParseException { JSONParser parser = new JSONParser(); - JSONObject jsonObject = (JSONObject)parser.parse(data); + JSONObject jsonObject = (JSONObject) parser.parse(data); - String name = (String)jsonObject.get("name"); - String user = (String)jsonObject.get("updatedBy"); - String containerData = (String)jsonObject.get("data"); + String name = (String) jsonObject.get("name"); + String user = (String) jsonObject.get("updatedBy"); + String containerData = (String) jsonObject.get("data"); Container item; - List containers = - containerRepository.findByNameAndServer(name, serverName); + List containers = containerRepository.findByNameAndServer(name, serverName); if (containers.isEmpty()) { item = new Container(name, user, containerData, serverName); } else { @@ -64,11 +70,9 @@ public String health() { @CrossOrigin @GetMapping("/container") - public Container - getContainerDetails(@NotNull @RequestParam("containerId") String name, - @NotNull @RequestParam("server") String server) { - List item = - containerRepository.findByNameAndServer(name, server); + public Container getContainerDetails(@NotNull @RequestParam("containerId") String name, + @NotNull @RequestParam("server") String server) { + List item = containerRepository.findByNameAndServer(name, server); if (item.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST); } @@ -77,14 +81,12 @@ public String health() { @CrossOrigin @DeleteMapping("/container") - public String - deleteContainerItem(@NotNull @RequestParam("containerId") String name, - @NotNull @RequestParam("server") String server) { - List containerList = - containerRepository.findByNameAndServer(name, server); + public String deleteContainerItem(@NotNull @RequestParam("containerId") String name, + @NotNull @RequestParam("server") String server) { + List containerList = containerRepository.findByNameAndServer(name, server); if (containerList.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, - "container does not exist"); + "container does not exist"); } Container container = containerList.get(0); for (Group group : container.getGroups()) { @@ -100,18 +102,16 @@ public String health() { public List getAllContainers( @RequestParam(value = "server", required = false) String server) { return server != null ? containerRepository.findByServer(server) - : containerRepository.findAll(); + : containerRepository.findAll(); } @GetMapping("/getContainerGroups") - public List - getContainerGroups(@RequestParam("server") String server, - @RequestParam("containerId") String name) { - List containerList = - containerRepository.findByNameAndServer(name, server); + public List getContainerGroups(@RequestParam("server") String server, + @RequestParam("containerId") String name) { + List containerList = containerRepository.findByNameAndServer(name, server); if (containerList.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, - "container does not exist"); + "container does not exist"); } Container container = containerList.get(0); return container.getGroups(); @@ -119,7 +119,7 @@ public List getAllContainers( @Autowired public ContainerController(ContainerRepository containerRepository, - GroupRepository groupRepository) { + GroupRepository groupRepository) { this.containerRepository = containerRepository; this.groupRepository = groupRepository; } diff --git a/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/GroupController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/GroupController.java similarity index 93% rename from src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/GroupController.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/GroupController.java index 70351ca..3015acb 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/controllers/v1/GroupController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/GroupController.java @@ -1,9 +1,9 @@ -package com.sms.stockmanagementsystem.project.controllers.v1; +package eu.goldenkoopa.stockmanagementsystem.controllers.v1; -import com.sms.stockmanagementsystem.project.data.Container; -import com.sms.stockmanagementsystem.project.data.Group; -import com.sms.stockmanagementsystem.project.repositories.ContainerRepository; -import com.sms.stockmanagementsystem.project.repositories.GroupRepository; +import eu.goldenkoopa.stockmanagementsystem.data.Container; +import eu.goldenkoopa.stockmanagementsystem.data.Group; +import eu.goldenkoopa.stockmanagementsystem.repositories.ContainerRepository; +import eu.goldenkoopa.stockmanagementsystem.repositories.GroupRepository; import java.util.List; import java.util.Optional; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java new file mode 100644 index 0000000..7077e8a --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java @@ -0,0 +1,40 @@ +package eu.goldenkoopa.stockmanagementsystem.controllers.v1; + +import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.PrivilegeDto; +import eu.goldenkoopa.stockmanagementsystem.services.PrivilegeService; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/privileges") +@Secured({"ROLE_ADMIN"}) +public class PrivilegeController { + + private PrivilegeService privilegeService; + + @GetMapping + public ResponseEntity> getAllPrivileges() { + return ResponseEntity.ok(privilegeService.getAllPrivileges() + .stream() + .map(PrivilegeDto::from) + .toList()); + } + + @DeleteMapping("/{id}") + public ResponseEntity deletePrivilege(@PathVariable Long id) { + privilegeService.deletePrivilege(id); + return ResponseEntity.noContent().build(); + } + + @Autowired + public PrivilegeController(PrivilegeService privilegeService) { + this.privilegeService = privilegeService; + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java new file mode 100644 index 0000000..5581178 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java @@ -0,0 +1,37 @@ +package eu.goldenkoopa.stockmanagementsystem.controllers.v1; + +import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.UserWithApiKeyDto; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; +import eu.goldenkoopa.stockmanagementsystem.services.UserService; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/users") +@Secured({"ROLE_ADMIN"}) +public class UserController { + + @Autowired private UserRepository userRepository; + + @GetMapping() + public List getAllUsers() { + return userRepository.findAll() + .stream() + .map(UserWithApiKeyDto::from) + .toList(); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteUser(@PathVariable Long id) { + UserService.deleteUser; + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Container.java similarity index 96% rename from src/main/java/com/sms/stockmanagementsystem/project/data/Container.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Container.java index 630b835..713583f 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/Container.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Container.java @@ -1,4 +1,4 @@ -package com.sms.stockmanagementsystem.project.data; +package eu.goldenkoopa.stockmanagementsystem.data; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/Group.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Group.java similarity index 95% rename from src/main/java/com/sms/stockmanagementsystem/project/data/Group.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Group.java index fb37c40..07bb0d5 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/Group.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Group.java @@ -1,4 +1,4 @@ -package com.sms.stockmanagementsystem.project.data; +package eu.goldenkoopa.stockmanagementsystem.data; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java new file mode 100644 index 0000000..21e07fb --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java @@ -0,0 +1,33 @@ +package eu.goldenkoopa.stockmanagementsystem.data.authentication; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; + +import java.util.List; +import lombok.Data; + +@Entity +@Data +public class ApiKey { + + @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; + + private String key; + + @ManyToOne + private User user; + + @ManyToMany + @JoinTable( + name = "apikeys_privileges", + joinColumns = @JoinColumn(name = "apikey_id", referencedColumnName = "id"), + inverseJoinColumns = + @JoinColumn(name = "privilege_id", referencedColumnName = "id")) + private List privileges; +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/Privilege.java similarity index 86% rename from src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/Privilege.java index c941d42..bdfabdc 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Privilege.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/Privilege.java @@ -1,4 +1,4 @@ -package com.sms.stockmanagementsystem.project.data.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.authentication; import jakarta.persistence.*; import java.util.Collection; diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/Role.java similarity index 91% rename from src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/Role.java index c5db04d..d3bc45b 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/Role.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/Role.java @@ -1,4 +1,4 @@ -package com.sms.stockmanagementsystem.project.data.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.authentication; import jakarta.persistence.*; import java.util.Collection; diff --git a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/User.java similarity index 74% rename from src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/User.java index bae9d11..d015fc1 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/data/authentication/User.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/User.java @@ -1,4 +1,4 @@ -package com.sms.stockmanagementsystem.project.data.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.authentication; import jakarta.persistence.*; import java.util.Collection; @@ -18,6 +18,8 @@ public class User { private boolean enabled; private boolean tokenExpired; + @OneToMany(mappedBy = "user") private Collection apiKeys; + @ManyToMany @JoinTable(name = "users_roles", joinColumns = @@ -25,4 +27,9 @@ public class User { inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) private Collection roles; + + @Override + public String toString() { + return id + username + firstName + lastName; + } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyDto.java new file mode 100644 index 0000000..c698ac9 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyDto.java @@ -0,0 +1,28 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; +import java.util.List; + +/** + * A data transfer object (DTO) representing an API key with associated + * privileges. + * This record encapsulates the essential information of an API key, including + * its unique identifier, the key itself, and a list of privileges associated + * with it. + */ +public record ApiKeyDto(Long id, String key, List privileges) { + + /** + * Creates an ApiKeyDto from an ApiKey entity. + * + * @param apiKey The ApiKey entity to convert + * @return A new ApiKeyDto instance containing the API key's information and + * associated privileges + * @throws NullPointerException if the apiKey parameter is null + */ + public static ApiKeyDto from(ApiKey apiKey) { + return new ApiKeyDto( + apiKey.getId(), apiKey.getKey(), + apiKey.getPrivileges().stream().map(PrivilegeDto::from).toList()); + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyWithUserDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyWithUserDto.java new file mode 100644 index 0000000..6fc3798 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyWithUserDto.java @@ -0,0 +1,34 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; +import java.util.List; + +/** + * A data transfer object (DTO) representing an API key with associated user + * information and privileges. This record encapsulates the essential + * information of an API key, including its unique identifier, the key itself, a + * list of privileges, and the associated user. + */ +public record ApiKeyWithUserDto(Long id, String key, + List privileges, UserDto user) { + + /** + * Creates an ApiKeyWithUserDto from an ApiKey entity. + * + * @param apiKey The ApiKey entity to convert + * @return A new ApiKeyWithUserDto instance containing the API key's + * information, privileges, and associated user + * @throws IllegalArgumentException if the apiKey is null, or if the key is + * null + * or blank + * @throws NullPointerException if the privileges or user in the apiKey + * are + * null + */ + public static ApiKeyWithUserDto from(ApiKey apiKey) { + return new ApiKeyWithUserDto( + apiKey.getId(), apiKey.getKey(), + apiKey.getPrivileges().stream().map(PrivilegeDto::from).toList(), + UserDto.from(apiKey.getUser())); + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/PrivilegeDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/PrivilegeDto.java new file mode 100644 index 0000000..36bb184 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/PrivilegeDto.java @@ -0,0 +1,22 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Privilege; + +/** + * A data transfer object (DTO) representing a privilege. + * This record encapsulates the essential information of a privilege, + * including its unique identifier and name. + */ +public record PrivilegeDto(Long id, String name) { + + /** + * Creates a PrivilegeDto from a Privilege entity. + * + * @param privilege The Privilege entity to convert + * @return A new PrivilegeDto instance containing the privilege's information + * @throws NullPointerException if the privilege parameter is null + */ + public static PrivilegeDto from(Privilege privilege) { + return new PrivilegeDto(privilege.getId(), privilege.getName()); + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java new file mode 100644 index 0000000..3f618da --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java @@ -0,0 +1,26 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; + +/** + * A data transfer object (DTO) representing a user. + * This record encapsulates the essential information of a user, + * including their unique identifier, first name, last name, and username. + */ +public record UserDto(Long id, String firstName, String lastName, + String username) { + + /** + * Creates a UserDto from a User entity. + * + * @param user The User entity to convert + * @return A new UserDto instance containing the user's information + * @throws IllegalArgumentException if the user is null or if any required + * field + * is null or blank + */ + public static UserDto from(User user) { + return new UserDto(user.getId(), user.getFirstName(), user.getLastName(), + user.getUsername()); + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java new file mode 100644 index 0000000..d51dfe1 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java @@ -0,0 +1,28 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; +import java.util.List; + +/** + * A data transfer object (DTO) representing a user with associated API keys. + * This record encapsulates user information along with a list of their API + * keys. + */ +public record UserWithApiKeyDto(Long id, String firstName, String lastName, + String username, List apiKeys) { + + /** + * Creates a UserWithApiKeyDto from a User entity. + * + * @param user The User entity to convert + * @return A new UserWithApiKeyDto instance containing the user's information + * and API keys + * @throws NullPointerException if the user parameter is null + */ + public static UserWithApiKeyDto from(User user) { + return new UserWithApiKeyDto( + user.getId(), user.getFirstName(), user.getLastName(), + user.getUsername(), + user.getApiKeys().stream().map(ApiKeyDto::from).toList()); + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/ContainerRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java similarity index 74% rename from src/main/java/com/sms/stockmanagementsystem/project/repositories/ContainerRepository.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java index 6c0c3bd..a072c97 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/ContainerRepository.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java @@ -1,6 +1,6 @@ -package com.sms.stockmanagementsystem.project.repositories; +package eu.goldenkoopa.stockmanagementsystem.repositories; -import com.sms.stockmanagementsystem.project.data.Container; +import eu.goldenkoopa.stockmanagementsystem.data.Container; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/GroupRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/GroupRepository.java similarity index 63% rename from src/main/java/com/sms/stockmanagementsystem/project/repositories/GroupRepository.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/GroupRepository.java index f9f9f1e..a9e99e6 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/GroupRepository.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/GroupRepository.java @@ -1,6 +1,6 @@ -package com.sms.stockmanagementsystem.project.repositories; +package eu.goldenkoopa.stockmanagementsystem.repositories; -import com.sms.stockmanagementsystem.project.data.Group; +import eu.goldenkoopa.stockmanagementsystem.data.Group; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java new file mode 100644 index 0000000..433de88 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java @@ -0,0 +1,17 @@ +package eu.goldenkoopa.stockmanagementsystem.repositories.authentication; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; + +@Repository +public interface ApiKeyRepository extends JpaRepository { + + List findByUser(User user); + + boolean existsByKey(String key); +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/PrivilegeRepository.java similarity index 61% rename from src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/PrivilegeRepository.java index b2b92a5..a548a34 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/PrivilegeRepository.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/PrivilegeRepository.java @@ -1,6 +1,6 @@ -package com.sms.stockmanagementsystem.project.repositories.authentication; +package eu.goldenkoopa.stockmanagementsystem.repositories.authentication; -import com.sms.stockmanagementsystem.project.data.authentication.Privilege; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Privilege; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/RoleRepository.java similarity index 61% rename from src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/RoleRepository.java index cfd9189..7fc2cd1 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/repositories/authentication/RoleRepository.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/RoleRepository.java @@ -1,6 +1,6 @@ -package com.sms.stockmanagementsystem.project.repositories.authentication; +package eu.goldenkoopa.stockmanagementsystem.repositories.authentication; -import com.sms.stockmanagementsystem.project.data.authentication.Role; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Role; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/UserRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/UserRepository.java new file mode 100644 index 0000000..1c16161 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/UserRepository.java @@ -0,0 +1,15 @@ +package eu.goldenkoopa.stockmanagementsystem.repositories.authentication; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + + Optional findByUsername(String username); + + List getByUsername(String username); +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java new file mode 100644 index 0000000..9b14f2d --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java @@ -0,0 +1,80 @@ +package eu.goldenkoopa.stockmanagementsystem.services; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.ApiKeyRepository; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.PrivilegeRepository; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; +import java.util.List; +import java.util.UUID; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * ApiKeyService + */ +@Service +public class ApiKeyService { + + private ApiKeyRepository apiKeyRepository; + private UserRepository userRepository; + private PrivilegeRepository privilegeRepository; + + public ApiKeyService(ApiKeyRepository apiKeyRepository, + UserRepository userRepository, + PrivilegeRepository privilegeRepository) { + this.apiKeyRepository = apiKeyRepository; + this.userRepository = userRepository; + this.privilegeRepository = privilegeRepository; + } + + /** + * @return list of all api keys + */ + public List getAllApiKeys() { + List apiKeys = apiKeyRepository.findAll(); + return apiKeys; + } + + @Transactional + public ApiKey generateNewApiKey(List privilegeIds, + UserDetails userDetails) { + if (privilegeIds == null || privilegeIds.isEmpty()) { + throw new IllegalArgumentException( + "Privilege IDs cannot be null or empty"); + } + User user = + userRepository.findByUsername(userDetails.getUsername()) + .orElseThrow( + () -> new UsernameNotFoundException("username not found")); + + ApiKey apiKey = new ApiKey(); + apiKey.setUser(user); + apiKey.setPrivileges( + privilegeIds.stream() + .map(id + -> privilegeRepository.findById(id).orElseThrow( + () -> new RuntimeException("privilege id not found"))) + .toList()); + // privilegeIds.forEach( + // (privilegeId) + // -> apiKey.getPrivileges().add( + // privilegeRepository.findById(privilegeId) + // .orElseThrow( + // () -> new RuntimeException("privilege id not found")))); + apiKey.setKey(generateUniqueApiKey()); + + ApiKey apiKeyResponse = apiKeyRepository.save(apiKey); + return apiKeyResponse; + } + + private String generateUniqueApiKey() { + String key; + do { + key = UUID.randomUUID().toString(); + } while (apiKeyRepository.existsByKey(key)); + return key; + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/PrivilegeService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/PrivilegeService.java new file mode 100644 index 0000000..3e21bb2 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/PrivilegeService.java @@ -0,0 +1,22 @@ +package eu.goldenkoopa.stockmanagementsystem.services; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Privilege; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.PrivilegeRepository; +import java.util.List; +import org.springframework.stereotype.Service; + +@Service +public class PrivilegeService { + + private PrivilegeRepository privilegeRepository; + + public List getAllPrivileges() { + return privilegeRepository.findAll(); + } + + public void deletePrivilege(Long id) { privilegeRepository.deleteById(id); } + + public PrivilegeService(PrivilegeRepository privilegeRepository) { + this.privilegeRepository = privilegeRepository; + } +} diff --git a/src/main/java/com/sms/stockmanagementsystem/project/services/SmsUserDetailsService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/SmsUserDetailsService.java similarity index 82% rename from src/main/java/com/sms/stockmanagementsystem/project/services/SmsUserDetailsService.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/services/SmsUserDetailsService.java index 52b53d8..60fc389 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/services/SmsUserDetailsService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/SmsUserDetailsService.java @@ -1,10 +1,10 @@ -package com.sms.stockmanagementsystem.project.services; +package eu.goldenkoopa.stockmanagementsystem.services; -import com.sms.stockmanagementsystem.project.data.authentication.Privilege; -import com.sms.stockmanagementsystem.project.data.authentication.Role; -import com.sms.stockmanagementsystem.project.data.authentication.User; -import com.sms.stockmanagementsystem.project.repositories.authentication.RoleRepository; -import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Privilege; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.Role; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.RoleRepository; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; import jakarta.transaction.Transactional; import java.util.ArrayList; import java.util.Arrays; @@ -24,9 +24,6 @@ public class SmsUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; - // @Autowired - // private IUserService service; - @Autowired private RoleRepository roleRepository; @Override diff --git a/src/main/java/com/sms/stockmanagementsystem/project/services/UserService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java similarity index 81% rename from src/main/java/com/sms/stockmanagementsystem/project/services/UserService.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java index 8fe2164..32f0c94 100644 --- a/src/main/java/com/sms/stockmanagementsystem/project/services/UserService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java @@ -1,7 +1,7 @@ -package com.sms.stockmanagementsystem.project.services; +package eu.goldenkoopa.stockmanagementsystem.services; -import com.sms.stockmanagementsystem.project.data.authentication.User; -import com.sms.stockmanagementsystem.project.repositories.authentication.UserRepository; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; +import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; @@ -55,4 +55,11 @@ public boolean existsByUsername(String username) { * @param user The user to save. */ public void saveUser(User user) { userRepository.save(user); } + + /** + * Deletes a user from the repository. + * + * @param id The id of the user to delete. + */ + public void deleteUser(Long id) { userRepository.deleteById(id); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/utils/AnnotationScanner.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/utils/AnnotationScanner.java new file mode 100644 index 0000000..b23467a --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/utils/AnnotationScanner.java @@ -0,0 +1,69 @@ +package eu.goldenkoopa.stockmanagementsystem.utils; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import org.springframework.security.access.annotation.Secured; + +public class AnnotationScanner { + public static List scan() throws Exception { + String packageName = "eu.goldenkoopa.stockmanagementsystem.controllers.v1"; + List> classes = getClasses(packageName); + List privileges = new ArrayList<>(); + + for (Class clazz : classes) { + if (clazz.isAnnotationPresent(Secured.class)) { + Secured annotation = clazz.getAnnotation(Secured.class); + Arrays.asList(annotation.value()).forEach(privilege -> { + if (!privileges.contains(privilege)) { + privileges.add(privilege); + } + }); + // System.out.println("Class: " + clazz.getName() + + // ", Value: " + Arrays.toString(annotation.value())); + } + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(Secured.class)) { + Secured annotation = method.getAnnotation(Secured.class); + Arrays.asList(annotation.value()).forEach(privilege -> { + if (!privileges.contains(privilege)) { + privileges.add(privilege); + } + }); + // System.out.println( + // "Class: " + clazz.getName() + ", Method: " + method.getName() + + // ", Value: " + Arrays.toString(annotation.value())); + } + } + } + System.out.println("Privileges found: " + privileges); + return privileges; + } + + // Method to scan classes from a package + private static List> getClasses(String packageName) + throws IOException, ClassNotFoundException { + List> classes = new ArrayList<>(); + String path = packageName.replace('.', '/'); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Enumeration resources = classLoader.getResources(path); + + while (resources.hasMoreElements()) { + File directory = new File(resources.nextElement().getFile()); + if (directory.exists()) { + for (String file : directory.list()) { + if (file.endsWith(".class")) { + String className = packageName + '.' + file.substring(0, file.length() - 6); + classes.add(Class.forName(className)); + } + } + } + } + return classes; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ca50b3e..ca85b8c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,5 +20,6 @@ server: logging.level: org.springframework.security: TRACE + org.springframework.web: DEBUG secret: ${SECRET} From 88654f30d9fe740dc3fc038e4aa61252ff7e1964 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:00:58 +0100 Subject: [PATCH 06/13] --wip-- [skip ci] --- .editorconfig | 1280 ----------------- .../controllers/v1/UserController.java | 19 +- .../services/UserService.java | 11 + 3 files changed, 25 insertions(+), 1285 deletions(-) diff --git a/.editorconfig b/.editorconfig index de636c5..7dfc609 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,1343 +6,63 @@ indent_style = space insert_final_newline = true max_line_length = 80 tab_width = 2 -ij_continuation_indent_size = 4 -ij_formatter_off_tag = @formatter:off -ij_formatter_on_tag = @formatter:on -ij_formatter_tags_enabled = false -ij_smart_tabs = false -ij_wrap_on_typing = false [.gitignore] max_line_length = unset -[*.css] -ij_css_align_closing_brace_with_properties = false -ij_css_blank_lines_around_nested_selector = 1 -ij_css_blank_lines_between_blocks = 1 -ij_css_brace_placement = 0 -ij_css_hex_color_long_format = false -ij_css_hex_color_lower_case = false -ij_css_hex_color_short_format = false -ij_css_hex_color_upper_case = false -ij_css_keep_blank_lines_in_code = 2 -ij_css_keep_indents_on_empty_lines = false -ij_css_keep_single_line_blocks = false -ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow -ij_css_space_after_colon = true -ij_css_space_before_opening_brace = true -ij_css_value_alignment = 0 - [*.md] max_line_length = unset [*.feature] tab_width = 4 -ij_continuation_indent_size = 8 -ij_gherkin_keep_indents_on_empty_lines = false [*.gsp] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_gsp_keep_indents_on_empty_lines = false [*.haml] tab_width = 4 -ij_continuation_indent_size = 8 -ij_haml_keep_indents_on_empty_lines = false - -[*.java] -ij_java_align_consecutive_assignments = false -ij_java_align_consecutive_variable_declarations = false -ij_java_align_group_field_declarations = false -ij_java_align_multiline_annotation_parameters = false -ij_java_align_multiline_array_initializer_expression = false -ij_java_align_multiline_assignment = false -ij_java_align_multiline_binary_operation = false -ij_java_align_multiline_chained_methods = false -ij_java_align_multiline_extends_list = false -ij_java_align_multiline_for = false -ij_java_align_multiline_method_parentheses = false -ij_java_align_multiline_parameters = false -ij_java_align_multiline_parameters_in_calls = false -ij_java_align_multiline_parenthesized_expression = false -ij_java_align_multiline_resources = false -ij_java_align_multiline_ternary_operation = false -ij_java_align_multiline_throws_list = false -ij_java_align_subsequent_simple_methods = false -ij_java_align_throws_keyword = false -ij_java_annotation_parameter_wrap = off -ij_java_array_initializer_new_line_after_left_brace = false -ij_java_array_initializer_right_brace_on_new_line = false -ij_java_array_initializer_wrap = normal -ij_java_assert_statement_colon_on_next_line = false -ij_java_assert_statement_wrap = off -ij_java_assignment_wrap = off -ij_java_binary_operation_sign_on_next_line = true -ij_java_binary_operation_wrap = normal -ij_java_blank_lines_after_anonymous_class_header = 0 -ij_java_blank_lines_after_class_header = 1 -ij_java_blank_lines_after_imports = 1 -ij_java_blank_lines_after_package = 1 -ij_java_blank_lines_around_class = 1 -ij_java_blank_lines_around_field = 0 -ij_java_blank_lines_around_field_in_interface = 0 -ij_java_blank_lines_around_initializer = 1 -ij_java_blank_lines_around_method = 1 -ij_java_blank_lines_around_method_in_interface = 1 -ij_java_blank_lines_before_class_end = 0 -ij_java_blank_lines_before_imports = 1 -ij_java_blank_lines_before_method_body = 0 -ij_java_blank_lines_before_package = 0 -ij_java_block_brace_style = end_of_line -ij_java_block_comment_at_first_column = true -ij_java_call_parameters_new_line_after_left_paren = false -ij_java_call_parameters_right_paren_on_new_line = false -ij_java_call_parameters_wrap = normal -ij_java_case_statement_on_separate_line = true -ij_java_catch_on_new_line = false -ij_java_class_annotation_wrap = split_into_lines -ij_java_class_brace_style = end_of_line -ij_java_class_count_to_use_import_on_demand = 999 -ij_java_class_names_in_javadoc = 1 -ij_java_do_not_indent_top_level_class_members = false -ij_java_do_not_wrap_after_single_annotation = false -ij_java_do_while_brace_force = always -ij_java_doc_add_blank_line_after_description = true -ij_java_doc_add_blank_line_after_param_comments = false -ij_java_doc_add_blank_line_after_return = false -ij_java_doc_add_p_tag_on_empty_lines = true -ij_java_doc_align_exception_comments = true -ij_java_doc_align_param_comments = true -ij_java_doc_do_not_wrap_if_one_line = false -ij_java_doc_enable_formatting = true -ij_java_doc_enable_leading_asterisks = true -ij_java_doc_indent_on_continuation = false -ij_java_doc_keep_empty_lines = true -ij_java_doc_keep_empty_parameter_tag = true -ij_java_doc_keep_empty_return_tag = true -ij_java_doc_keep_empty_throws_tag = true -ij_java_doc_keep_invalid_tags = true -ij_java_doc_param_description_on_new_line = false -ij_java_doc_preserve_line_breaks = false -ij_java_doc_use_throws_not_exception_tag = true -ij_java_else_on_new_line = false -ij_java_entity_dd_suffix = EJB -ij_java_entity_eb_suffix = Bean -ij_java_entity_hi_suffix = Home -ij_java_entity_lhi_prefix = Local -ij_java_entity_lhi_suffix = Home -ij_java_entity_li_prefix = Local -ij_java_entity_pk_class = java.lang.String -ij_java_entity_vo_suffix = VO -ij_java_enum_constants_wrap = off -ij_java_extends_keyword_wrap = off -ij_java_extends_list_wrap = normal -ij_java_field_annotation_wrap = split_into_lines -ij_java_finally_on_new_line = false -ij_java_for_brace_force = always -ij_java_for_statement_new_line_after_left_paren = false -ij_java_for_statement_right_paren_on_new_line = false -ij_java_for_statement_wrap = normal -ij_java_generate_final_locals = false -ij_java_generate_final_parameters = false -ij_java_if_brace_force = always -ij_java_imports_layout = $*,|,* -ij_java_indent_case_from_switch = true -ij_java_insert_inner_class_imports = true -ij_java_insert_override_annotation = true -ij_java_keep_blank_lines_before_right_brace = 2 -ij_java_keep_blank_lines_between_package_declaration_and_header = 2 -ij_java_keep_blank_lines_in_code = 1 -ij_java_keep_blank_lines_in_declarations = 2 -ij_java_keep_control_statement_in_one_line = false -ij_java_keep_first_column_comment = true -ij_java_keep_indents_on_empty_lines = false -ij_java_keep_line_breaks = true -ij_java_keep_multiple_expressions_in_one_line = false -ij_java_keep_simple_blocks_in_one_line = false -ij_java_keep_simple_classes_in_one_line = false -ij_java_keep_simple_lambdas_in_one_line = false -ij_java_keep_simple_methods_in_one_line = false -ij_java_lambda_brace_style = end_of_line -ij_java_layout_static_imports_separately = true -ij_java_line_comment_add_space = false -ij_java_line_comment_at_first_column = true -ij_java_message_dd_suffix = EJB -ij_java_message_eb_suffix = Bean -ij_java_method_annotation_wrap = split_into_lines -ij_java_method_brace_style = end_of_line -ij_java_method_call_chain_wrap = normal -ij_java_method_parameters_new_line_after_left_paren = false -ij_java_method_parameters_right_paren_on_new_line = false -ij_java_method_parameters_wrap = normal -ij_java_modifier_list_wrap = false -ij_java_names_count_to_use_import_on_demand = 999 -ij_java_parameter_annotation_wrap = off -ij_java_parentheses_expression_new_line_after_left_paren = false -ij_java_parentheses_expression_right_paren_on_new_line = false -ij_java_place_assignment_sign_on_next_line = false -ij_java_prefer_longer_names = true -ij_java_prefer_parameters_wrap = false -ij_java_repeat_synchronized = true -ij_java_replace_instanceof_and_cast = false -ij_java_replace_null_check = true -ij_java_replace_sum_lambda_with_method_ref = true -ij_java_resource_list_new_line_after_left_paren = false -ij_java_resource_list_right_paren_on_new_line = false -ij_java_resource_list_wrap = off -ij_java_session_dd_suffix = EJB -ij_java_session_eb_suffix = Bean -ij_java_session_hi_suffix = Home -ij_java_session_lhi_prefix = Local -ij_java_session_lhi_suffix = Home -ij_java_session_li_prefix = Local -ij_java_session_si_suffix = Service -ij_java_space_after_closing_angle_bracket_in_type_argument = false -ij_java_space_after_colon = true -ij_java_space_after_comma = true -ij_java_space_after_comma_in_type_arguments = true -ij_java_space_after_for_semicolon = true -ij_java_space_after_quest = true -ij_java_space_after_type_cast = true -ij_java_space_before_annotation_array_initializer_left_brace = false -ij_java_space_before_annotation_parameter_list = false -ij_java_space_before_array_initializer_left_brace = false -ij_java_space_before_catch_keyword = true -ij_java_space_before_catch_left_brace = true -ij_java_space_before_catch_parentheses = true -ij_java_space_before_class_left_brace = true -ij_java_space_before_colon = true -ij_java_space_before_colon_in_foreach = true -ij_java_space_before_comma = false -ij_java_space_before_do_left_brace = true -ij_java_space_before_else_keyword = true -ij_java_space_before_else_left_brace = true -ij_java_space_before_finally_keyword = true -ij_java_space_before_finally_left_brace = true -ij_java_space_before_for_left_brace = true -ij_java_space_before_for_parentheses = true -ij_java_space_before_for_semicolon = false -ij_java_space_before_if_left_brace = true -ij_java_space_before_if_parentheses = true -ij_java_space_before_method_call_parentheses = false -ij_java_space_before_method_left_brace = true -ij_java_space_before_method_parentheses = false -ij_java_space_before_opening_angle_bracket_in_type_parameter = false -ij_java_space_before_quest = true -ij_java_space_before_switch_left_brace = true -ij_java_space_before_switch_parentheses = true -ij_java_space_before_synchronized_left_brace = true -ij_java_space_before_synchronized_parentheses = true -ij_java_space_before_try_left_brace = true -ij_java_space_before_try_parentheses = true -ij_java_space_before_type_parameter_list = false -ij_java_space_before_while_keyword = true -ij_java_space_before_while_left_brace = true -ij_java_space_before_while_parentheses = true -ij_java_space_inside_one_line_enum_braces = false -ij_java_space_within_empty_array_initializer_braces = false -ij_java_space_within_empty_method_call_parentheses = false -ij_java_space_within_empty_method_parentheses = false -ij_java_spaces_around_additive_operators = true -ij_java_spaces_around_assignment_operators = true -ij_java_spaces_around_bitwise_operators = true -ij_java_spaces_around_equality_operators = true -ij_java_spaces_around_lambda_arrow = true -ij_java_spaces_around_logical_operators = true -ij_java_spaces_around_method_ref_dbl_colon = false -ij_java_spaces_around_multiplicative_operators = true -ij_java_spaces_around_relational_operators = true -ij_java_spaces_around_shift_operators = true -ij_java_spaces_around_type_bounds_in_type_parameters = true -ij_java_spaces_around_unary_operator = false -ij_java_spaces_within_angle_brackets = false -ij_java_spaces_within_annotation_parentheses = false -ij_java_spaces_within_array_initializer_braces = false -ij_java_spaces_within_braces = false -ij_java_spaces_within_brackets = false -ij_java_spaces_within_cast_parentheses = false -ij_java_spaces_within_catch_parentheses = false -ij_java_spaces_within_for_parentheses = false -ij_java_spaces_within_if_parentheses = false -ij_java_spaces_within_method_call_parentheses = false -ij_java_spaces_within_method_parentheses = false -ij_java_spaces_within_parentheses = false -ij_java_spaces_within_switch_parentheses = false -ij_java_spaces_within_synchronized_parentheses = false -ij_java_spaces_within_try_parentheses = false -ij_java_spaces_within_while_parentheses = false -ij_java_special_else_if_treatment = true -ij_java_subclass_name_suffix = Impl -ij_java_ternary_operation_signs_on_next_line = true -ij_java_ternary_operation_wrap = normal -ij_java_test_name_suffix = Test -ij_java_throws_keyword_wrap = normal -ij_java_throws_list_wrap = off -ij_java_use_external_annotations = false -ij_java_use_fq_class_names = false -ij_java_use_single_class_imports = true -ij_java_variable_annotation_wrap = off -ij_java_visibility = public -ij_java_while_brace_force = always -ij_java_while_on_new_line = false -ij_java_wrap_comments = true -ij_java_wrap_first_method_in_call_chain = false -ij_java_wrap_long_lines = false [*.less] tab_width = 4 -ij_continuation_indent_size = 8 -ij_less_align_closing_brace_with_properties = false -ij_less_blank_lines_around_nested_selector = 1 -ij_less_blank_lines_between_blocks = 1 -ij_less_brace_placement = 0 -ij_less_hex_color_long_format = false -ij_less_hex_color_lower_case = false -ij_less_hex_color_short_format = false -ij_less_hex_color_upper_case = false -ij_less_keep_blank_lines_in_code = 2 -ij_less_keep_indents_on_empty_lines = false -ij_less_keep_single_line_blocks = false -ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow -ij_less_space_after_colon = true -ij_less_space_before_opening_brace = true -ij_less_value_alignment = 0 - -[*.sass] -ij_sass_align_closing_brace_with_properties = false -ij_sass_blank_lines_around_nested_selector = 1 -ij_sass_blank_lines_between_blocks = 1 -ij_sass_brace_placement = 0 -ij_sass_hex_color_long_format = false -ij_sass_hex_color_lower_case = false -ij_sass_hex_color_short_format = false -ij_sass_hex_color_upper_case = false -ij_sass_keep_blank_lines_in_code = 2 -ij_sass_keep_indents_on_empty_lines = false -ij_sass_keep_single_line_blocks = false -ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow -ij_sass_space_after_colon = true -ij_sass_space_before_opening_brace = true -ij_sass_value_alignment = 0 - -[*.scss] -ij_scss_align_closing_brace_with_properties = false -ij_scss_blank_lines_around_nested_selector = 1 -ij_scss_blank_lines_between_blocks = 1 -ij_scss_brace_placement = 0 -ij_scss_hex_color_long_format = false -ij_scss_hex_color_lower_case = false -ij_scss_hex_color_short_format = false -ij_scss_hex_color_upper_case = false -ij_scss_keep_blank_lines_in_code = 2 -ij_scss_keep_indents_on_empty_lines = false -ij_scss_keep_single_line_blocks = false -ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow -ij_scss_space_after_colon = true -ij_scss_space_before_opening_brace = true -ij_scss_value_alignment = 0 [*.styl] tab_width = 4 -ij_continuation_indent_size = 8 -ij_stylus_align_closing_brace_with_properties = false -ij_stylus_blank_lines_around_nested_selector = 1 -ij_stylus_blank_lines_between_blocks = 1 -ij_stylus_brace_placement = 0 -ij_stylus_hex_color_long_format = false -ij_stylus_hex_color_lower_case = false -ij_stylus_hex_color_short_format = false -ij_stylus_hex_color_upper_case = false -ij_stylus_keep_blank_lines_in_code = 2 -ij_stylus_keep_indents_on_empty_lines = false -ij_stylus_keep_single_line_blocks = false -ij_stylus_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow -ij_stylus_space_after_colon = true -ij_stylus_space_before_opening_brace = true -ij_stylus_value_alignment = 0 [.editorconfig] max_line_length = unset -ij_editorconfig_align_group_field_declarations = false -ij_editorconfig_space_after_colon = false -ij_editorconfig_space_after_comma = true -ij_editorconfig_space_before_colon = false -ij_editorconfig_space_before_comma = false -ij_editorconfig_spaces_around_assignment_operators = true [{*.as,*.js2,*.es}] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_actionscript_align_imports = false -ij_actionscript_align_multiline_array_initializer_expression = false -ij_actionscript_align_multiline_binary_operation = false -ij_actionscript_align_multiline_chained_methods = false -ij_actionscript_align_multiline_extends_list = false -ij_actionscript_align_multiline_for = false -ij_actionscript_align_multiline_parameters = false -ij_actionscript_align_multiline_parameters_in_calls = false -ij_actionscript_align_multiline_ternary_operation = false -ij_actionscript_align_object_properties = 0 -ij_actionscript_align_union_types = false -ij_actionscript_align_var_statements = 0 -ij_actionscript_array_initializer_new_line_after_left_brace = false -ij_actionscript_array_initializer_right_brace_on_new_line = false -ij_actionscript_array_initializer_wrap = normal -ij_actionscript_assignment_wrap = off -ij_actionscript_binary_operation_sign_on_next_line = true -ij_actionscript_binary_operation_wrap = normal -ij_actionscript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** -ij_actionscript_blank_lines_after_imports = 1 -ij_actionscript_blank_lines_after_package = 0 -ij_actionscript_blank_lines_around_function = 1 -ij_actionscript_blank_lines_around_method = 1 -ij_actionscript_blank_lines_before_imports = 1 -ij_actionscript_blank_lines_before_package = 0 -ij_actionscript_block_brace_style = end_of_line -ij_actionscript_call_parameters_new_line_after_left_paren = false -ij_actionscript_call_parameters_right_paren_on_new_line = false -ij_actionscript_call_parameters_wrap = normal -ij_actionscript_catch_on_new_line = false -ij_actionscript_chained_call_dot_on_new_line = true -ij_actionscript_class_brace_style = end_of_line -ij_actionscript_comma_on_new_line = false -ij_actionscript_do_while_brace_force = always -ij_actionscript_else_on_new_line = false -ij_actionscript_enforce_trailing_comma = keep -ij_actionscript_extends_keyword_wrap = off -ij_actionscript_extends_list_wrap = normal -ij_actionscript_field_prefix = _ -ij_actionscript_file_name_style = relaxed -ij_actionscript_finally_on_new_line = false -ij_actionscript_for_brace_force = always -ij_actionscript_for_statement_new_line_after_left_paren = false -ij_actionscript_for_statement_right_paren_on_new_line = false -ij_actionscript_for_statement_wrap = normal -ij_actionscript_force_quote_style = false -ij_actionscript_force_semicolon_style = false -ij_actionscript_function_expression_brace_style = end_of_line -ij_actionscript_if_brace_force = always -ij_actionscript_import_merge_members = global -ij_actionscript_import_prefer_absolute_path = global -ij_actionscript_import_sort_members = true -ij_actionscript_import_sort_module_name = false -ij_actionscript_import_use_node_resolution = true -ij_actionscript_imports_wrap = on_every_item -ij_actionscript_indent_case_from_switch = true -ij_actionscript_indent_chained_calls = true -ij_actionscript_indent_package_children = 0 -ij_actionscript_jsx_attribute_value = braces -ij_actionscript_keep_blank_lines_in_code = 1 -ij_actionscript_keep_first_column_comment = true -ij_actionscript_keep_indents_on_empty_lines = false -ij_actionscript_keep_line_breaks = true -ij_actionscript_keep_simple_blocks_in_one_line = false -ij_actionscript_keep_simple_methods_in_one_line = false -ij_actionscript_line_comment_at_first_column = true -ij_actionscript_method_brace_style = end_of_line -ij_actionscript_method_call_chain_wrap = off -ij_actionscript_method_parameters_new_line_after_left_paren = false -ij_actionscript_method_parameters_right_paren_on_new_line = false -ij_actionscript_method_parameters_wrap = normal -ij_actionscript_object_literal_wrap = on_every_item -ij_actionscript_parentheses_expression_new_line_after_left_paren = false -ij_actionscript_parentheses_expression_right_paren_on_new_line = false -ij_actionscript_place_assignment_sign_on_next_line = false -ij_actionscript_prefer_as_type_cast = false -ij_actionscript_prefer_parameters_wrap = false -ij_actionscript_reformat_c_style_comments = false -ij_actionscript_space_after_colon = true -ij_actionscript_space_after_comma = true -ij_actionscript_space_after_dots_in_rest_parameter = false -ij_actionscript_space_after_generator_mult = true -ij_actionscript_space_after_property_colon = true -ij_actionscript_space_after_quest = true -ij_actionscript_space_after_type_colon = false -ij_actionscript_space_after_unary_not = false -ij_actionscript_space_before_async_arrow_lparen = true -ij_actionscript_space_before_catch_keyword = true -ij_actionscript_space_before_catch_left_brace = true -ij_actionscript_space_before_catch_parentheses = true -ij_actionscript_space_before_class_lbrace = true -ij_actionscript_space_before_colon = true -ij_actionscript_space_before_comma = false -ij_actionscript_space_before_do_left_brace = true -ij_actionscript_space_before_else_keyword = true -ij_actionscript_space_before_else_left_brace = true -ij_actionscript_space_before_finally_keyword = true -ij_actionscript_space_before_finally_left_brace = true -ij_actionscript_space_before_for_left_brace = true -ij_actionscript_space_before_for_parentheses = true -ij_actionscript_space_before_for_semicolon = false -ij_actionscript_space_before_function_left_parenth = true -ij_actionscript_space_before_generator_mult = false -ij_actionscript_space_before_if_left_brace = true -ij_actionscript_space_before_if_parentheses = true -ij_actionscript_space_before_method_call_parentheses = false -ij_actionscript_space_before_method_left_brace = true -ij_actionscript_space_before_method_parentheses = false -ij_actionscript_space_before_property_colon = false -ij_actionscript_space_before_quest = true -ij_actionscript_space_before_switch_left_brace = true -ij_actionscript_space_before_switch_parentheses = true -ij_actionscript_space_before_try_left_brace = true -ij_actionscript_space_before_type_colon = false -ij_actionscript_space_before_unary_not = false -ij_actionscript_space_before_while_keyword = true -ij_actionscript_space_before_while_left_brace = true -ij_actionscript_space_before_while_parentheses = true -ij_actionscript_spaces_around_additive_operators = true -ij_actionscript_spaces_around_arrow_function_operator = true -ij_actionscript_spaces_around_assignment_operators = true -ij_actionscript_spaces_around_bitwise_operators = true -ij_actionscript_spaces_around_equality_operators = true -ij_actionscript_spaces_around_logical_operators = true -ij_actionscript_spaces_around_multiplicative_operators = true -ij_actionscript_spaces_around_relational_operators = true -ij_actionscript_spaces_around_shift_operators = true -ij_actionscript_spaces_around_unary_operator = false -ij_actionscript_spaces_within_array_initializer_brackets = false -ij_actionscript_spaces_within_brackets = false -ij_actionscript_spaces_within_catch_parentheses = false -ij_actionscript_spaces_within_for_parentheses = false -ij_actionscript_spaces_within_if_parentheses = false -ij_actionscript_spaces_within_imports = false -ij_actionscript_spaces_within_interpolation_expressions = false -ij_actionscript_spaces_within_method_call_parentheses = false -ij_actionscript_spaces_within_method_parentheses = false -ij_actionscript_spaces_within_object_literal_braces = false -ij_actionscript_spaces_within_object_type_braces = true -ij_actionscript_spaces_within_parentheses = false -ij_actionscript_spaces_within_switch_parentheses = false -ij_actionscript_spaces_within_type_assertion = false -ij_actionscript_spaces_within_union_types = true -ij_actionscript_spaces_within_while_parentheses = false -ij_actionscript_special_else_if_treatment = true -ij_actionscript_ternary_operation_signs_on_next_line = true -ij_actionscript_ternary_operation_wrap = normal -ij_actionscript_union_types_wrap = on_every_item -ij_actionscript_use_chained_calls_group_indents = false -ij_actionscript_use_double_quotes = true -ij_actionscript_use_explicit_js_extension = global -ij_actionscript_use_path_mapping = always -ij_actionscript_use_public_modifier = false -ij_actionscript_use_semicolon_after_statement = true -ij_actionscript_var_declaration_wrap = normal -ij_actionscript_while_brace_force = always -ij_actionscript_while_on_new_line = false -ij_actionscript_wrap_comments = false - -[{*.ats,*.ts}] -ij_typescript_align_imports = false -ij_typescript_align_multiline_array_initializer_expression = false -ij_typescript_align_multiline_binary_operation = false -ij_typescript_align_multiline_chained_methods = false -ij_typescript_align_multiline_extends_list = false -ij_typescript_align_multiline_for = true -ij_typescript_align_multiline_parameters = true -ij_typescript_align_multiline_parameters_in_calls = false -ij_typescript_align_multiline_ternary_operation = false -ij_typescript_align_object_properties = 0 -ij_typescript_align_union_types = false -ij_typescript_align_var_statements = 0 -ij_typescript_array_initializer_new_line_after_left_brace = false -ij_typescript_array_initializer_right_brace_on_new_line = false -ij_typescript_array_initializer_wrap = off -ij_typescript_assignment_wrap = off -ij_typescript_binary_operation_sign_on_next_line = false -ij_typescript_binary_operation_wrap = off -ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** -ij_typescript_blank_lines_after_imports = 1 -ij_typescript_blank_lines_around_class = 1 -ij_typescript_blank_lines_around_field = 0 -ij_typescript_blank_lines_around_field_in_interface = 0 -ij_typescript_blank_lines_around_function = 1 -ij_typescript_blank_lines_around_method = 1 -ij_typescript_blank_lines_around_method_in_interface = 1 -ij_typescript_block_brace_style = end_of_line -ij_typescript_call_parameters_new_line_after_left_paren = false -ij_typescript_call_parameters_right_paren_on_new_line = false -ij_typescript_call_parameters_wrap = off -ij_typescript_catch_on_new_line = false -ij_typescript_chained_call_dot_on_new_line = true -ij_typescript_class_brace_style = end_of_line -ij_typescript_comma_on_new_line = false -ij_typescript_do_while_brace_force = never -ij_typescript_else_on_new_line = false -ij_typescript_enforce_trailing_comma = keep -ij_typescript_extends_keyword_wrap = off -ij_typescript_extends_list_wrap = off -ij_typescript_field_prefix = _ -ij_typescript_file_name_style = relaxed -ij_typescript_finally_on_new_line = false -ij_typescript_for_brace_force = never -ij_typescript_for_statement_new_line_after_left_paren = false -ij_typescript_for_statement_right_paren_on_new_line = false -ij_typescript_for_statement_wrap = off -ij_typescript_force_quote_style = false -ij_typescript_force_semicolon_style = false -ij_typescript_function_expression_brace_style = end_of_line -ij_typescript_if_brace_force = never -ij_typescript_import_merge_members = global -ij_typescript_import_prefer_absolute_path = global -ij_typescript_import_sort_members = true -ij_typescript_import_sort_module_name = false -ij_typescript_import_use_node_resolution = true -ij_typescript_imports_wrap = on_every_item -ij_typescript_indent_case_from_switch = true -ij_typescript_indent_chained_calls = false -ij_typescript_indent_package_children = 0 -ij_typescript_jsdoc_include_types = false -ij_typescript_jsx_attribute_value = braces -ij_typescript_keep_blank_lines_in_code = 2 -ij_typescript_keep_first_column_comment = true -ij_typescript_keep_indents_on_empty_lines = false -ij_typescript_keep_line_breaks = true -ij_typescript_keep_simple_blocks_in_one_line = false -ij_typescript_keep_simple_methods_in_one_line = false -ij_typescript_line_comment_add_space = true -ij_typescript_line_comment_at_first_column = false -ij_typescript_method_brace_style = end_of_line -ij_typescript_method_call_chain_wrap = off -ij_typescript_method_parameters_new_line_after_left_paren = false -ij_typescript_method_parameters_right_paren_on_new_line = false -ij_typescript_method_parameters_wrap = off -ij_typescript_object_literal_wrap = on_every_item -ij_typescript_parentheses_expression_new_line_after_left_paren = false -ij_typescript_parentheses_expression_right_paren_on_new_line = false -ij_typescript_place_assignment_sign_on_next_line = false -ij_typescript_prefer_as_type_cast = false -ij_typescript_prefer_parameters_wrap = false -ij_typescript_reformat_c_style_comments = false -ij_typescript_space_after_colon = true -ij_typescript_space_after_comma = true -ij_typescript_space_after_dots_in_rest_parameter = false -ij_typescript_space_after_generator_mult = true -ij_typescript_space_after_property_colon = true -ij_typescript_space_after_quest = true -ij_typescript_space_after_type_colon = true -ij_typescript_space_after_unary_not = false -ij_typescript_space_before_async_arrow_lparen = true -ij_typescript_space_before_catch_keyword = true -ij_typescript_space_before_catch_left_brace = true -ij_typescript_space_before_catch_parentheses = true -ij_typescript_space_before_class_lbrace = true -ij_typescript_space_before_class_left_brace = true -ij_typescript_space_before_colon = true -ij_typescript_space_before_comma = false -ij_typescript_space_before_do_left_brace = true -ij_typescript_space_before_else_keyword = true -ij_typescript_space_before_else_left_brace = true -ij_typescript_space_before_finally_keyword = true -ij_typescript_space_before_finally_left_brace = true -ij_typescript_space_before_for_left_brace = true -ij_typescript_space_before_for_parentheses = true -ij_typescript_space_before_for_semicolon = false -ij_typescript_space_before_function_left_parenth = true -ij_typescript_space_before_generator_mult = false -ij_typescript_space_before_if_left_brace = true -ij_typescript_space_before_if_parentheses = true -ij_typescript_space_before_method_call_parentheses = false -ij_typescript_space_before_method_left_brace = true -ij_typescript_space_before_method_parentheses = false -ij_typescript_space_before_property_colon = false -ij_typescript_space_before_quest = true -ij_typescript_space_before_switch_left_brace = true -ij_typescript_space_before_switch_parentheses = true -ij_typescript_space_before_try_left_brace = true -ij_typescript_space_before_type_colon = false -ij_typescript_space_before_unary_not = false -ij_typescript_space_before_while_keyword = true -ij_typescript_space_before_while_left_brace = true -ij_typescript_space_before_while_parentheses = true -ij_typescript_spaces_around_additive_operators = true -ij_typescript_spaces_around_arrow_function_operator = true -ij_typescript_spaces_around_assignment_operators = true -ij_typescript_spaces_around_bitwise_operators = true -ij_typescript_spaces_around_equality_operators = true -ij_typescript_spaces_around_logical_operators = true -ij_typescript_spaces_around_multiplicative_operators = true -ij_typescript_spaces_around_relational_operators = true -ij_typescript_spaces_around_shift_operators = true -ij_typescript_spaces_around_unary_operator = false -ij_typescript_spaces_within_array_initializer_brackets = false -ij_typescript_spaces_within_brackets = false -ij_typescript_spaces_within_catch_parentheses = false -ij_typescript_spaces_within_for_parentheses = false -ij_typescript_spaces_within_if_parentheses = false -ij_typescript_spaces_within_imports = false -ij_typescript_spaces_within_interpolation_expressions = false -ij_typescript_spaces_within_method_call_parentheses = false -ij_typescript_spaces_within_method_parentheses = false -ij_typescript_spaces_within_object_literal_braces = false -ij_typescript_spaces_within_object_type_braces = true -ij_typescript_spaces_within_parentheses = false -ij_typescript_spaces_within_switch_parentheses = false -ij_typescript_spaces_within_type_assertion = false -ij_typescript_spaces_within_union_types = true -ij_typescript_spaces_within_while_parentheses = false -ij_typescript_special_else_if_treatment = true -ij_typescript_ternary_operation_signs_on_next_line = false -ij_typescript_ternary_operation_wrap = off -ij_typescript_union_types_wrap = on_every_item -ij_typescript_use_chained_calls_group_indents = false -ij_typescript_use_double_quotes = true -ij_typescript_use_explicit_js_extension = global -ij_typescript_use_path_mapping = always -ij_typescript_use_public_modifier = false -ij_typescript_use_semicolon_after_statement = true -ij_typescript_var_declaration_wrap = normal -ij_typescript_while_brace_force = never -ij_typescript_while_on_new_line = false -ij_typescript_wrap_comments = false [{*.cfml,*.cfm,*.cfc}] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_cfml_align_multiline_binary_operation = false -ij_cfml_align_multiline_for = true -ij_cfml_align_multiline_parameters = true -ij_cfml_align_multiline_parameters_in_calls = false -ij_cfml_align_multiline_ternary_operation = false -ij_cfml_assignment_wrap = off -ij_cfml_binary_operation_sign_on_next_line = false -ij_cfml_binary_operation_wrap = off -ij_cfml_block_brace_style = end_of_line -ij_cfml_call_parameters_new_line_after_left_paren = false -ij_cfml_call_parameters_right_paren_on_new_line = false -ij_cfml_call_parameters_wrap = off -ij_cfml_catch_on_new_line = false -ij_cfml_else_on_new_line = false -ij_cfml_for_statement_new_line_after_left_paren = false -ij_cfml_for_statement_right_paren_on_new_line = false -ij_cfml_for_statement_wrap = off -ij_cfml_keep_blank_lines_in_code = 2 -ij_cfml_keep_first_column_comment = true -ij_cfml_keep_indents_on_empty_lines = false -ij_cfml_keep_line_breaks = true -ij_cfml_method_brace_style = next_line -ij_cfml_method_parameters_new_line_after_left_paren = false -ij_cfml_method_parameters_right_paren_on_new_line = false -ij_cfml_method_parameters_wrap = off -ij_cfml_parentheses_expression_new_line_after_left_paren = false -ij_cfml_parentheses_expression_right_paren_on_new_line = false -ij_cfml_place_assignment_sign_on_next_line = false -ij_cfml_space_after_colon = true -ij_cfml_space_after_comma = true -ij_cfml_space_after_for_semicolon = true -ij_cfml_space_after_quest = true -ij_cfml_space_before_catch_keyword = true -ij_cfml_space_before_catch_left_brace = true -ij_cfml_space_before_catch_parentheses = true -ij_cfml_space_before_colon = true -ij_cfml_space_before_comma = false -ij_cfml_space_before_else_keyword = true -ij_cfml_space_before_else_left_brace = true -ij_cfml_space_before_for_left_brace = true -ij_cfml_space_before_for_parentheses = true -ij_cfml_space_before_for_semicolon = false -ij_cfml_space_before_if_left_brace = true -ij_cfml_space_before_if_parentheses = true -ij_cfml_space_before_method_call_parentheses = false -ij_cfml_space_before_method_left_brace = true -ij_cfml_space_before_method_parentheses = false -ij_cfml_space_before_quest = true -ij_cfml_space_before_switch_left_brace = true -ij_cfml_space_before_switch_parentheses = true -ij_cfml_space_before_try_left_brace = true -ij_cfml_space_before_while_keyword = true -ij_cfml_space_before_while_left_brace = true -ij_cfml_space_before_while_parentheses = true -ij_cfml_spaces_around_additive_operators = true -ij_cfml_spaces_around_assignment_operators = true -ij_cfml_spaces_around_equality_operators = true -ij_cfml_spaces_around_logical_operators = true -ij_cfml_spaces_around_multiplicative_operators = true -ij_cfml_spaces_around_relational_operators = true -ij_cfml_spaces_around_unary_operator = false -ij_cfml_spaces_within_catch_parentheses = false -ij_cfml_spaces_within_for_parentheses = false -ij_cfml_spaces_within_if_parentheses = false -ij_cfml_spaces_within_method_call_parentheses = false -ij_cfml_spaces_within_method_parentheses = false -ij_cfml_spaces_within_switch_parentheses = false -ij_cfml_spaces_within_while_parentheses = false -ij_cfml_special_else_if_treatment = false -ij_cfml_ternary_operation_signs_on_next_line = false -ij_cfml_ternary_operation_wrap = off -ij_cfml_while_on_new_line = false [{*.cjs,*.js}] max_line_length = 80 -ij_javascript_align_imports = false -ij_javascript_align_multiline_array_initializer_expression = false -ij_javascript_align_multiline_binary_operation = false -ij_javascript_align_multiline_chained_methods = false -ij_javascript_align_multiline_extends_list = false -ij_javascript_align_multiline_for = false -ij_javascript_align_multiline_parameters = false -ij_javascript_align_multiline_parameters_in_calls = false -ij_javascript_align_multiline_ternary_operation = false -ij_javascript_align_object_properties = 0 -ij_javascript_align_union_types = false -ij_javascript_align_var_statements = 0 -ij_javascript_array_initializer_new_line_after_left_brace = false -ij_javascript_array_initializer_right_brace_on_new_line = false -ij_javascript_array_initializer_wrap = normal -ij_javascript_assignment_wrap = off -ij_javascript_binary_operation_sign_on_next_line = true -ij_javascript_binary_operation_wrap = normal -ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** -ij_javascript_blank_lines_after_imports = 1 -ij_javascript_blank_lines_around_class = 1 -ij_javascript_blank_lines_around_field = 0 -ij_javascript_blank_lines_around_function = 1 -ij_javascript_blank_lines_around_method = 1 -ij_javascript_block_brace_style = end_of_line -ij_javascript_call_parameters_new_line_after_left_paren = false -ij_javascript_call_parameters_right_paren_on_new_line = false -ij_javascript_call_parameters_wrap = normal -ij_javascript_catch_on_new_line = false -ij_javascript_chained_call_dot_on_new_line = true -ij_javascript_class_brace_style = end_of_line -ij_javascript_comma_on_new_line = false -ij_javascript_do_while_brace_force = always -ij_javascript_else_on_new_line = false -ij_javascript_enforce_trailing_comma = keep -ij_javascript_extends_keyword_wrap = off -ij_javascript_extends_list_wrap = off -ij_javascript_field_prefix = _ -ij_javascript_file_name_style = relaxed -ij_javascript_finally_on_new_line = false -ij_javascript_for_brace_force = always -ij_javascript_for_statement_new_line_after_left_paren = false -ij_javascript_for_statement_right_paren_on_new_line = false -ij_javascript_for_statement_wrap = normal -ij_javascript_force_quote_style = false -ij_javascript_force_semicolon_style = false -ij_javascript_function_expression_brace_style = end_of_line -ij_javascript_if_brace_force = always -ij_javascript_import_merge_members = global -ij_javascript_import_prefer_absolute_path = global -ij_javascript_import_sort_members = true -ij_javascript_import_sort_module_name = false -ij_javascript_import_use_node_resolution = true -ij_javascript_imports_wrap = on_every_item -ij_javascript_indent_case_from_switch = true -ij_javascript_indent_chained_calls = false -ij_javascript_indent_package_children = 0 -ij_javascript_jsx_attribute_value = braces -ij_javascript_keep_blank_lines_in_code = 1 -ij_javascript_keep_first_column_comment = true -ij_javascript_keep_indents_on_empty_lines = false -ij_javascript_keep_line_breaks = true -ij_javascript_keep_simple_blocks_in_one_line = false -ij_javascript_keep_simple_methods_in_one_line = false -ij_javascript_line_comment_add_space = true -ij_javascript_line_comment_at_first_column = false -ij_javascript_method_brace_style = end_of_line -ij_javascript_method_call_chain_wrap = off -ij_javascript_method_parameters_new_line_after_left_paren = false -ij_javascript_method_parameters_right_paren_on_new_line = false -ij_javascript_method_parameters_wrap = normal -ij_javascript_object_literal_wrap = on_every_item -ij_javascript_parentheses_expression_new_line_after_left_paren = false -ij_javascript_parentheses_expression_right_paren_on_new_line = false -ij_javascript_place_assignment_sign_on_next_line = false -ij_javascript_prefer_as_type_cast = false -ij_javascript_prefer_parameters_wrap = false -ij_javascript_reformat_c_style_comments = false -ij_javascript_space_after_colon = true -ij_javascript_space_after_comma = true -ij_javascript_space_after_dots_in_rest_parameter = false -ij_javascript_space_after_generator_mult = true -ij_javascript_space_after_property_colon = true -ij_javascript_space_after_quest = true -ij_javascript_space_after_type_colon = true -ij_javascript_space_after_unary_not = false -ij_javascript_space_before_async_arrow_lparen = true -ij_javascript_space_before_catch_keyword = true -ij_javascript_space_before_catch_left_brace = true -ij_javascript_space_before_catch_parentheses = true -ij_javascript_space_before_class_lbrace = true -ij_javascript_space_before_class_left_brace = true -ij_javascript_space_before_colon = true -ij_javascript_space_before_comma = false -ij_javascript_space_before_do_left_brace = true -ij_javascript_space_before_else_keyword = true -ij_javascript_space_before_else_left_brace = true -ij_javascript_space_before_finally_keyword = true -ij_javascript_space_before_finally_left_brace = true -ij_javascript_space_before_for_left_brace = true -ij_javascript_space_before_for_parentheses = true -ij_javascript_space_before_for_semicolon = false -ij_javascript_space_before_function_left_parenth = true -ij_javascript_space_before_generator_mult = false -ij_javascript_space_before_if_left_brace = true -ij_javascript_space_before_if_parentheses = true -ij_javascript_space_before_method_call_parentheses = false -ij_javascript_space_before_method_left_brace = true -ij_javascript_space_before_method_parentheses = false -ij_javascript_space_before_property_colon = false -ij_javascript_space_before_quest = true -ij_javascript_space_before_switch_left_brace = true -ij_javascript_space_before_switch_parentheses = true -ij_javascript_space_before_try_left_brace = true -ij_javascript_space_before_type_colon = false -ij_javascript_space_before_unary_not = false -ij_javascript_space_before_while_keyword = true -ij_javascript_space_before_while_left_brace = true -ij_javascript_space_before_while_parentheses = true -ij_javascript_spaces_around_additive_operators = true -ij_javascript_spaces_around_arrow_function_operator = true -ij_javascript_spaces_around_assignment_operators = true -ij_javascript_spaces_around_bitwise_operators = true -ij_javascript_spaces_around_equality_operators = true -ij_javascript_spaces_around_logical_operators = true -ij_javascript_spaces_around_multiplicative_operators = true -ij_javascript_spaces_around_relational_operators = true -ij_javascript_spaces_around_shift_operators = true -ij_javascript_spaces_around_unary_operator = false -ij_javascript_spaces_within_array_initializer_brackets = false -ij_javascript_spaces_within_brackets = false -ij_javascript_spaces_within_catch_parentheses = false -ij_javascript_spaces_within_for_parentheses = false -ij_javascript_spaces_within_if_parentheses = false -ij_javascript_spaces_within_imports = false -ij_javascript_spaces_within_interpolation_expressions = false -ij_javascript_spaces_within_method_call_parentheses = false -ij_javascript_spaces_within_method_parentheses = false -ij_javascript_spaces_within_object_literal_braces = false -ij_javascript_spaces_within_object_type_braces = true -ij_javascript_spaces_within_parentheses = false -ij_javascript_spaces_within_switch_parentheses = false -ij_javascript_spaces_within_type_assertion = false -ij_javascript_spaces_within_union_types = true -ij_javascript_spaces_within_while_parentheses = false -ij_javascript_special_else_if_treatment = true -ij_javascript_ternary_operation_signs_on_next_line = true -ij_javascript_ternary_operation_wrap = normal -ij_javascript_union_types_wrap = on_every_item -ij_javascript_use_chained_calls_group_indents = false -ij_javascript_use_double_quotes = true -ij_javascript_use_explicit_js_extension = global -ij_javascript_use_path_mapping = always -ij_javascript_use_public_modifier = false -ij_javascript_use_semicolon_after_statement = true -ij_javascript_var_declaration_wrap = normal -ij_javascript_while_brace_force = always -ij_javascript_while_on_new_line = false -ij_javascript_wrap_comments = false - -[{*.cjsx,*.coffee}] -ij_continuation_indent_size = 2 -ij_coffeescript_align_function_body = false -ij_coffeescript_align_imports = false -ij_coffeescript_align_multiline_array_initializer_expression = true -ij_coffeescript_align_multiline_parameters = true -ij_coffeescript_align_multiline_parameters_in_calls = false -ij_coffeescript_align_object_properties = 0 -ij_coffeescript_align_union_types = false -ij_coffeescript_align_var_statements = 0 -ij_coffeescript_array_initializer_new_line_after_left_brace = false -ij_coffeescript_array_initializer_right_brace_on_new_line = false -ij_coffeescript_array_initializer_wrap = normal -ij_coffeescript_blacklist_imports = rxjs/Rx,node_modules/**/*,@angular/material,@angular/material/typings/** -ij_coffeescript_blank_lines_around_function = 1 -ij_coffeescript_call_parameters_new_line_after_left_paren = false -ij_coffeescript_call_parameters_right_paren_on_new_line = false -ij_coffeescript_call_parameters_wrap = normal -ij_coffeescript_chained_call_dot_on_new_line = true -ij_coffeescript_comma_on_new_line = false -ij_coffeescript_enforce_trailing_comma = keep -ij_coffeescript_field_prefix = _ -ij_coffeescript_file_name_style = relaxed -ij_coffeescript_force_quote_style = false -ij_coffeescript_force_semicolon_style = false -ij_coffeescript_function_expression_brace_style = end_of_line -ij_coffeescript_import_merge_members = global -ij_coffeescript_import_prefer_absolute_path = global -ij_coffeescript_import_sort_members = true -ij_coffeescript_import_sort_module_name = false -ij_coffeescript_import_use_node_resolution = true -ij_coffeescript_imports_wrap = on_every_item -ij_coffeescript_indent_chained_calls = true -ij_coffeescript_indent_package_children = 0 -ij_coffeescript_jsx_attribute_value = braces -ij_coffeescript_keep_blank_lines_in_code = 2 -ij_coffeescript_keep_first_column_comment = true -ij_coffeescript_keep_indents_on_empty_lines = false -ij_coffeescript_keep_line_breaks = true -ij_coffeescript_keep_simple_methods_in_one_line = false -ij_coffeescript_method_parameters_new_line_after_left_paren = false -ij_coffeescript_method_parameters_right_paren_on_new_line = false -ij_coffeescript_method_parameters_wrap = off -ij_coffeescript_object_literal_wrap = on_every_item -ij_coffeescript_prefer_as_type_cast = false -ij_coffeescript_reformat_c_style_comments = false -ij_coffeescript_space_after_comma = true -ij_coffeescript_space_after_dots_in_rest_parameter = false -ij_coffeescript_space_after_generator_mult = true -ij_coffeescript_space_after_property_colon = true -ij_coffeescript_space_after_type_colon = true -ij_coffeescript_space_after_unary_not = false -ij_coffeescript_space_before_async_arrow_lparen = true -ij_coffeescript_space_before_class_lbrace = true -ij_coffeescript_space_before_comma = false -ij_coffeescript_space_before_function_left_parenth = true -ij_coffeescript_space_before_generator_mult = false -ij_coffeescript_space_before_property_colon = false -ij_coffeescript_space_before_type_colon = false -ij_coffeescript_space_before_unary_not = false -ij_coffeescript_spaces_around_additive_operators = true -ij_coffeescript_spaces_around_arrow_function_operator = true -ij_coffeescript_spaces_around_assignment_operators = true -ij_coffeescript_spaces_around_bitwise_operators = true -ij_coffeescript_spaces_around_equality_operators = true -ij_coffeescript_spaces_around_logical_operators = true -ij_coffeescript_spaces_around_multiplicative_operators = true -ij_coffeescript_spaces_around_relational_operators = true -ij_coffeescript_spaces_around_shift_operators = true -ij_coffeescript_spaces_around_unary_operator = false -ij_coffeescript_spaces_within_array_initializer_braces = false -ij_coffeescript_spaces_within_array_initializer_brackets = false -ij_coffeescript_spaces_within_imports = false -ij_coffeescript_spaces_within_index_brackets = false -ij_coffeescript_spaces_within_interpolation_expressions = false -ij_coffeescript_spaces_within_method_call_parentheses = false -ij_coffeescript_spaces_within_method_parentheses = false -ij_coffeescript_spaces_within_object_braces = false -ij_coffeescript_spaces_within_object_literal_braces = false -ij_coffeescript_spaces_within_object_type_braces = true -ij_coffeescript_spaces_within_range_brackets = false -ij_coffeescript_spaces_within_type_assertion = false -ij_coffeescript_spaces_within_union_types = true -ij_coffeescript_union_types_wrap = on_every_item -ij_coffeescript_use_chained_calls_group_indents = false -ij_coffeescript_use_double_quotes = true -ij_coffeescript_use_explicit_js_extension = global -ij_coffeescript_use_path_mapping = always -ij_coffeescript_use_public_modifier = false -ij_coffeescript_use_semicolon_after_statement = false -ij_coffeescript_var_declaration_wrap = normal [{*.gradle,*.groovy,*.gant,*.gdsl,*.gy,*.gson}] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_groovy_align_group_field_declarations = false -ij_groovy_align_multiline_array_initializer_expression = false -ij_groovy_align_multiline_assignment = false -ij_groovy_align_multiline_binary_operation = false -ij_groovy_align_multiline_chained_methods = false -ij_groovy_align_multiline_extends_list = false -ij_groovy_align_multiline_for = true -ij_groovy_align_multiline_method_parentheses = false -ij_groovy_align_multiline_parameters = true -ij_groovy_align_multiline_parameters_in_calls = false -ij_groovy_align_multiline_resources = true -ij_groovy_align_multiline_ternary_operation = false -ij_groovy_align_multiline_throws_list = false -ij_groovy_align_throws_keyword = false -ij_groovy_array_initializer_new_line_after_left_brace = false -ij_groovy_array_initializer_right_brace_on_new_line = false -ij_groovy_array_initializer_wrap = off -ij_groovy_assert_statement_wrap = off -ij_groovy_assignment_wrap = off -ij_groovy_binary_operation_wrap = off -ij_groovy_blank_lines_after_class_header = 0 -ij_groovy_blank_lines_after_imports = 1 -ij_groovy_blank_lines_after_package = 1 -ij_groovy_blank_lines_around_class = 1 -ij_groovy_blank_lines_around_field = 0 -ij_groovy_blank_lines_around_field_in_interface = 0 -ij_groovy_blank_lines_around_method = 1 -ij_groovy_blank_lines_around_method_in_interface = 1 -ij_groovy_blank_lines_before_imports = 1 -ij_groovy_blank_lines_before_method_body = 0 -ij_groovy_blank_lines_before_package = 0 -ij_groovy_block_brace_style = end_of_line -ij_groovy_block_comment_at_first_column = true -ij_groovy_call_parameters_new_line_after_left_paren = false -ij_groovy_call_parameters_right_paren_on_new_line = false -ij_groovy_call_parameters_wrap = off -ij_groovy_catch_on_new_line = false -ij_groovy_class_annotation_wrap = split_into_lines -ij_groovy_class_brace_style = end_of_line -ij_groovy_do_while_brace_force = never -ij_groovy_else_on_new_line = false -ij_groovy_enum_constants_wrap = off -ij_groovy_extends_keyword_wrap = off -ij_groovy_extends_list_wrap = off -ij_groovy_field_annotation_wrap = split_into_lines -ij_groovy_finally_on_new_line = false -ij_groovy_for_brace_force = never -ij_groovy_for_statement_new_line_after_left_paren = false -ij_groovy_for_statement_right_paren_on_new_line = false -ij_groovy_for_statement_wrap = off -ij_groovy_if_brace_force = never -ij_groovy_indent_case_from_switch = true -ij_groovy_keep_blank_lines_before_right_brace = 2 -ij_groovy_keep_blank_lines_in_code = 2 -ij_groovy_keep_blank_lines_in_declarations = 2 -ij_groovy_keep_control_statement_in_one_line = true -ij_groovy_keep_first_column_comment = true -ij_groovy_keep_indents_on_empty_lines = false -ij_groovy_keep_line_breaks = true -ij_groovy_keep_multiple_expressions_in_one_line = false -ij_groovy_keep_simple_blocks_in_one_line = false -ij_groovy_keep_simple_classes_in_one_line = true -ij_groovy_keep_simple_lambdas_in_one_line = true -ij_groovy_keep_simple_methods_in_one_line = true -ij_groovy_lambda_brace_style = end_of_line -ij_groovy_line_comment_add_space = false -ij_groovy_line_comment_at_first_column = true -ij_groovy_method_annotation_wrap = split_into_lines -ij_groovy_method_brace_style = end_of_line -ij_groovy_method_call_chain_wrap = off -ij_groovy_method_parameters_new_line_after_left_paren = false -ij_groovy_method_parameters_right_paren_on_new_line = false -ij_groovy_method_parameters_wrap = off -ij_groovy_modifier_list_wrap = false -ij_groovy_parameter_annotation_wrap = off -ij_groovy_parentheses_expression_new_line_after_left_paren = false -ij_groovy_parentheses_expression_right_paren_on_new_line = false -ij_groovy_prefer_parameters_wrap = false -ij_groovy_resource_list_new_line_after_left_paren = false -ij_groovy_resource_list_right_paren_on_new_line = false -ij_groovy_resource_list_wrap = off -ij_groovy_space_after_colon = true -ij_groovy_space_after_comma = true -ij_groovy_space_after_comma_in_type_arguments = true -ij_groovy_space_after_for_semicolon = true -ij_groovy_space_after_quest = true -ij_groovy_space_after_type_cast = true -ij_groovy_space_before_annotation_parameter_list = false -ij_groovy_space_before_array_initializer_left_brace = false -ij_groovy_space_before_catch_keyword = true -ij_groovy_space_before_catch_left_brace = true -ij_groovy_space_before_catch_parentheses = true -ij_groovy_space_before_class_left_brace = true -ij_groovy_space_before_colon = true -ij_groovy_space_before_comma = false -ij_groovy_space_before_do_left_brace = true -ij_groovy_space_before_else_keyword = true -ij_groovy_space_before_else_left_brace = true -ij_groovy_space_before_finally_keyword = true -ij_groovy_space_before_finally_left_brace = true -ij_groovy_space_before_for_left_brace = true -ij_groovy_space_before_for_parentheses = true -ij_groovy_space_before_for_semicolon = false -ij_groovy_space_before_if_left_brace = true -ij_groovy_space_before_if_parentheses = true -ij_groovy_space_before_method_call_parentheses = false -ij_groovy_space_before_method_left_brace = true -ij_groovy_space_before_method_parentheses = false -ij_groovy_space_before_quest = true -ij_groovy_space_before_switch_left_brace = true -ij_groovy_space_before_switch_parentheses = true -ij_groovy_space_before_synchronized_left_brace = true -ij_groovy_space_before_synchronized_parentheses = true -ij_groovy_space_before_try_left_brace = true -ij_groovy_space_before_try_parentheses = true -ij_groovy_space_before_while_keyword = true -ij_groovy_space_before_while_left_brace = true -ij_groovy_space_before_while_parentheses = true -ij_groovy_space_within_empty_array_initializer_braces = false -ij_groovy_space_within_empty_method_call_parentheses = false -ij_groovy_spaces_around_additive_operators = true -ij_groovy_spaces_around_assignment_operators = true -ij_groovy_spaces_around_bitwise_operators = true -ij_groovy_spaces_around_equality_operators = true -ij_groovy_spaces_around_lambda_arrow = true -ij_groovy_spaces_around_logical_operators = true -ij_groovy_spaces_around_multiplicative_operators = true -ij_groovy_spaces_around_relational_operators = true -ij_groovy_spaces_around_shift_operators = true -ij_groovy_spaces_within_annotation_parentheses = false -ij_groovy_spaces_within_array_initializer_braces = false -ij_groovy_spaces_within_braces = true -ij_groovy_spaces_within_brackets = false -ij_groovy_spaces_within_cast_parentheses = false -ij_groovy_spaces_within_catch_parentheses = false -ij_groovy_spaces_within_for_parentheses = false -ij_groovy_spaces_within_if_parentheses = false -ij_groovy_spaces_within_method_call_parentheses = false -ij_groovy_spaces_within_method_parentheses = false -ij_groovy_spaces_within_parentheses = false -ij_groovy_spaces_within_switch_parentheses = false -ij_groovy_spaces_within_synchronized_parentheses = false -ij_groovy_spaces_within_try_parentheses = false -ij_groovy_spaces_within_while_parentheses = false -ij_groovy_special_else_if_treatment = true -ij_groovy_ternary_operation_wrap = off -ij_groovy_throws_keyword_wrap = off -ij_groovy_throws_list_wrap = off -ij_groovy_variable_annotation_wrap = off -ij_groovy_while_brace_force = never -ij_groovy_while_on_new_line = false -ij_groovy_wrap_long_lines = false - -[{*.jhm,*.xjb,*.rng,*.wsdd,*.wsdl,*.fxml,*.bpmn,*.pom,*.xslt,*.jrxml,*.ant,*.xul,*.xsl,*.xsd,*.tld,*.jnlp,*.wadl,*.xml}] -ij_continuation_indent_size = 2 -ij_xml_block_comment_at_first_column = true -ij_xml_keep_indents_on_empty_lines = false -ij_xml_line_comment_at_first_column = true [{*.jspx,*.tagx}] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_jspx_keep_indents_on_empty_lines = false [{*.kts,*.kt}] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_kotlin_align_in_columns_case_branch = false -ij_kotlin_align_multiline_binary_operation = false -ij_kotlin_align_multiline_extends_list = false -ij_kotlin_align_multiline_method_parentheses = false -ij_kotlin_align_multiline_parameters = true -ij_kotlin_align_multiline_parameters_in_calls = false -ij_kotlin_assignment_wrap = off -ij_kotlin_blank_lines_after_class_header = 0 -ij_kotlin_blank_lines_around_block_when_branches = 0 -ij_kotlin_block_comment_at_first_column = true -ij_kotlin_call_parameters_new_line_after_left_paren = false -ij_kotlin_call_parameters_right_paren_on_new_line = false -ij_kotlin_call_parameters_wrap = off -ij_kotlin_catch_on_new_line = false -ij_kotlin_class_annotation_wrap = split_into_lines -ij_kotlin_continuation_indent_for_chained_calls = true -ij_kotlin_continuation_indent_for_expression_bodies = true -ij_kotlin_continuation_indent_in_argument_lists = true -ij_kotlin_continuation_indent_in_elvis = true -ij_kotlin_continuation_indent_in_if_conditions = true -ij_kotlin_continuation_indent_in_parameter_lists = true -ij_kotlin_continuation_indent_in_supertype_lists = true -ij_kotlin_else_on_new_line = false -ij_kotlin_enum_constants_wrap = off -ij_kotlin_extends_list_wrap = off -ij_kotlin_field_annotation_wrap = split_into_lines -ij_kotlin_finally_on_new_line = false -ij_kotlin_if_rparen_on_new_line = false -ij_kotlin_import_nested_classes = false -ij_kotlin_insert_whitespaces_in_simple_one_line_method = true -ij_kotlin_keep_blank_lines_before_right_brace = 2 -ij_kotlin_keep_blank_lines_in_code = 2 -ij_kotlin_keep_blank_lines_in_declarations = 2 -ij_kotlin_keep_first_column_comment = true -ij_kotlin_keep_indents_on_empty_lines = false -ij_kotlin_keep_line_breaks = true -ij_kotlin_lbrace_on_next_line = false -ij_kotlin_line_comment_add_space = false -ij_kotlin_line_comment_at_first_column = true -ij_kotlin_method_annotation_wrap = split_into_lines -ij_kotlin_method_call_chain_wrap = off -ij_kotlin_method_parameters_new_line_after_left_paren = false -ij_kotlin_method_parameters_right_paren_on_new_line = false -ij_kotlin_method_parameters_wrap = off -ij_kotlin_name_count_to_use_star_import = 5 -ij_kotlin_name_count_to_use_star_import_for_members = 3 -ij_kotlin_parameter_annotation_wrap = off -ij_kotlin_space_after_comma = true -ij_kotlin_space_after_extend_colon = true -ij_kotlin_space_after_type_colon = true -ij_kotlin_space_before_catch_parentheses = true -ij_kotlin_space_before_comma = false -ij_kotlin_space_before_extend_colon = true -ij_kotlin_space_before_for_parentheses = true -ij_kotlin_space_before_if_parentheses = true -ij_kotlin_space_before_lambda_arrow = true -ij_kotlin_space_before_type_colon = false -ij_kotlin_space_before_when_parentheses = true -ij_kotlin_space_before_while_parentheses = true -ij_kotlin_spaces_around_additive_operators = true -ij_kotlin_spaces_around_assignment_operators = true -ij_kotlin_spaces_around_equality_operators = true -ij_kotlin_spaces_around_function_type_arrow = true -ij_kotlin_spaces_around_logical_operators = true -ij_kotlin_spaces_around_multiplicative_operators = true -ij_kotlin_spaces_around_range = false -ij_kotlin_spaces_around_relational_operators = true -ij_kotlin_spaces_around_unary_operator = false -ij_kotlin_spaces_around_when_arrow = true -ij_kotlin_variable_annotation_wrap = off -ij_kotlin_while_on_new_line = false -ij_kotlin_wrap_elvis_expressions = 1 -ij_kotlin_wrap_expression_body_functions = 0 -ij_kotlin_wrap_first_method_in_call_chain = false - -[{*.sht,*.htm,*.html,*.shtm,*.shtml,*.ng}] -ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 -ij_html_align_attributes = true -ij_html_align_text = false -ij_html_attribute_wrap = normal -ij_html_block_comment_at_first_column = true -ij_html_do_not_align_children_of_min_lines = 0 -ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p -ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot -ij_html_enforce_quotes = false -ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var -ij_html_keep_blank_lines = 2 -ij_html_keep_indents_on_empty_lines = false -ij_html_keep_line_breaks = true -ij_html_keep_line_breaks_in_text = true -ij_html_keep_whitespaces = false -ij_html_keep_whitespaces_inside = span,pre,textarea -ij_html_line_comment_at_first_column = true -ij_html_new_line_after_last_attribute = never -ij_html_new_line_before_first_attribute = never -ij_html_quote_style = double -ij_html_remove_new_line_before_tags = br -ij_html_space_after_tag_name = false -ij_html_space_around_equality_in_attribute = false -ij_html_space_inside_empty_tag = false -ij_html_text_wrap = normal [{*.vsl,*.vm,*.ft}] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_vtl_keep_indents_on_empty_lines = false [{*.xjsp,*.tag,*.jsf,*.jsp,*.jspf,*.tagf}] indent_size = 4 tab_width = 4 -ij_continuation_indent_size = 8 -ij_jsp_jsp_prefer_comma_separated_import_list = false -ij_jsp_keep_indents_on_empty_lines = false [{*.yml,*.yaml}] tab_width = 4 -ij_continuation_indent_size = 2 -ij_yaml_keep_indents_on_empty_lines = false -ij_yaml_keep_line_breaks = true - -[{*.zsh,*.bash,*.sh}] -ij_shell_binary_ops_start_line = false -ij_shell_keep_column_alignment_padding = false -ij_shell_minify_program = false -ij_shell_redirect_followed_by_space = false -ij_shell_switch_cases_indented = false - -[{.eslintrc,.babelrc,jest.config,.stylelintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}] -ij_json_keep_blank_lines_in_code = 0 -ij_json_keep_indents_on_empty_lines = false -ij_json_keep_line_breaks = true -ij_json_space_after_colon = true -ij_json_space_after_comma = true -ij_json_space_before_colon = true -ij_json_space_before_comma = false -ij_json_spaces_within_braces = false -ij_json_spaces_within_brackets = false -ij_json_wrap_long_lines = false - -[{messages.*,spring.schemas,spring.handlers,messages,*.properties}] -ij_properties_align_group_field_declarations = false diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java index 5581178..34efd6e 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java @@ -1,9 +1,7 @@ package eu.goldenkoopa.stockmanagementsystem.controllers.v1; import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.UserWithApiKeyDto; -import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; import eu.goldenkoopa.stockmanagementsystem.services.UserService; - import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -19,19 +17,30 @@ @Secured({"ROLE_ADMIN"}) public class UserController { - @Autowired private UserRepository userRepository; + private UserService userService; @GetMapping() public List getAllUsers() { - return userRepository.findAll() + return userService.getAllUsers() .stream() .map(UserWithApiKeyDto::from) .toList(); } + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable Long id) { + return ResponseEntity.ok( + UserWithApiKeyDto.from(userService.getUserById(id))); + } + @DeleteMapping("/{id}") public ResponseEntity deleteUser(@PathVariable Long id) { - UserService.deleteUser; + userService.deleteUser(id); return ResponseEntity.noContent().build(); } + + @Autowired + public UserController(UserService userService) { + this.userService = userService; + } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java index 32f0c94..fd81674 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java @@ -2,6 +2,7 @@ import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; @@ -49,6 +50,11 @@ public boolean existsByUsername(String username) { return userRepository.findByUsername(username).isPresent(); } + public User getUserById(Long id) { + return userRepository.findById(id).orElseThrow( + () -> new RuntimeException("id not found")); + } + /** * Saves a user to the repository. * @@ -62,4 +68,9 @@ public boolean existsByUsername(String username) { * @param id The id of the user to delete. */ public void deleteUser(Long id) { userRepository.deleteById(id); } + + /** + * Gets all users from the repository. + */ + public List getAllUsers() { return userRepository.findAll(); } } From efbbbcde31ad1476027b75dfc913c87dbdba5296 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Fri, 28 Feb 2025 00:32:17 +0100 Subject: [PATCH 07/13] --wip-- [skip ci] --- .../ApiKeyAuthenticationToken.java | 43 +++++++++++++++ .../stockmanagementsystem/ApiKeyFilter.java | 52 +++++++++++++++++++ .../stockmanagementsystem/SecurityConfig.java | 7 +++ .../controllers/v1/ApiKeyController.java | 13 ++--- .../controllers/v1/ContainerController.java | 2 +- .../data/authentication/ApiKey.java | 20 +++---- .../authentication/ApiKeyRepository.java | 3 ++ .../services/ApiKeyService.java | 16 +++--- 8 files changed, 133 insertions(+), 23 deletions(-) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyAuthenticationToken.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyFilter.java diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyAuthenticationToken.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyAuthenticationToken.java new file mode 100644 index 0000000..7445476 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyAuthenticationToken.java @@ -0,0 +1,43 @@ +package eu.goldenkoopa.stockmanagementsystem; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; +import java.util.stream.Collectors; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; + +/** + * ApiKeyAuthenticationToken + */ +public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken { + + private ApiKey apiKey; + + public ApiKeyAuthenticationToken(ApiKey apiKey) { + super(AuthorityUtils.createAuthorityList( + apiKey.getPrivileges() + .stream() + .map(privilege -> privilege.getName()) + .toList())); + this.apiKey = apiKey; + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getPrincipal() { + return apiKey; + } + + @Override + public boolean isAuthenticated() { + return true; + } + + @Override + public void setAuthenticated(boolean authenticated) { + throw new RuntimeException("cannot modify"); + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyFilter.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyFilter.java new file mode 100644 index 0000000..f1afc90 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/ApiKeyFilter.java @@ -0,0 +1,52 @@ +package eu.goldenkoopa.stockmanagementsystem; + +import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; +import eu.goldenkoopa.stockmanagementsystem.services.ApiKeyService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +public class ApiKeyFilter extends OncePerRequestFilter { + + private ApiKeyService apiKeyService; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + Map pathVariables = request.getParameterMap(); + if (!pathVariables.keySet().contains("apiKey")) { + filterChain.doFilter(request, response); + return; + } + + ApiKey apiKey; + try { + apiKey = apiKeyService.getApiKeyByKey(pathVariables.get("apiKey")[0]); + } catch (Exception e) { + response.setStatus(HttpStatus.FORBIDDEN.value()); + response.getWriter().write("api key not valid"); + return; + } + + ApiKeyAuthenticationToken auth = new ApiKeyAuthenticationToken(apiKey); + SecurityContext newContext = SecurityContextHolder.createEmptyContext(); + newContext.setAuthentication(auth); + SecurityContextHolder.setContext(newContext); + + System.out.println("test"); + filterChain.doFilter(request, response); + } + + public ApiKeyFilter(ApiKeyService apiKeyService) { + this.apiKeyService = apiKeyService; + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/SecurityConfig.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SecurityConfig.java index 174af31..72463a1 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/SecurityConfig.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SecurityConfig.java @@ -1,5 +1,6 @@ package eu.goldenkoopa.stockmanagementsystem; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; @@ -9,12 +10,17 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.intercept.AuthorizationFilter; + +import eu.goldenkoopa.stockmanagementsystem.services.ApiKeyService; @Configuration @EnableWebSecurity @EnableMethodSecurity(securedEnabled = true) public class SecurityConfig { + @Autowired + private ApiKeyService apiKeyService; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -26,6 +32,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) }) .formLogin(Customizer.withDefaults()) .logout(logout -> logout.permitAll()) + .addFilterBefore(new ApiKeyFilter(apiKeyService), AuthorizationFilter.class) .csrf((csrf) -> csrf.ignoringRequestMatchers("/api/**")) // .csrf(csrf // -> csrf.csrfTokenRepository( diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java index 6b35e7a..045d9d9 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java @@ -44,16 +44,17 @@ public ResponseEntity> getAllApiKeys() { @AuthenticationPrincipal UserDetails userDetails) { ApiKeyWithUserDto apiKey = ApiKeyWithUserDto.from(apiKeyService.generateNewApiKey( - apiKeyRequest.privilegeIds(), userDetails)); + apiKeyRequest.privilegeIds(), apiKeyRequest.expirationTime(), userDetails)); return new ResponseEntity<>(apiKey, HttpStatus.CREATED); } - // private record ApiKeyRequest(List privilegeIds) {} private record ApiKeyRequest( - @NotNull(message = "Privilege IDs cannot be null test") - @NotEmpty(message = "Privilege IDs cannot be empty test") - List<@NotNull(message = "Privilege ID cannot be null test") Long> - privilegeIds) {} + @NotNull(message = "Privilege IDs cannot be null") + @NotEmpty(message = "Privilege IDs cannot be empty") + List<@NotNull(message = "Privilege ID cannot be null") Long> privilegeIds, + @NotNull(message = "Expiration time cannot be null") + @NotEmpty(message = "Expiration time cannot be empty") + Long expirationTime) {} @Autowired public ApiKeyController(ApiKeyService apiKeyService) { diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java index d9aff60..175c159 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java @@ -33,7 +33,7 @@ public class ContainerController { private final GroupRepository groupRepository; - @Secured({ "WRITE_PRIVILEGE" }) + @Secured({ "API_HEALTH_READ" }) @GetMapping("/health") public String health() { return "alive"; diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java index 21e07fb..c0d6a22 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/ApiKey.java @@ -1,6 +1,7 @@ package eu.goldenkoopa.stockmanagementsystem.data.authentication; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -8,7 +9,7 @@ import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; - +import java.time.LocalDate; import java.util.List; import lombok.Data; @@ -20,14 +21,15 @@ public class ApiKey { private String key; - @ManyToOne - private User user; + @ManyToOne private User user; + + private LocalDate expirationDate; - @ManyToMany - @JoinTable( - name = "apikeys_privileges", - joinColumns = @JoinColumn(name = "apikey_id", referencedColumnName = "id"), - inverseJoinColumns = - @JoinColumn(name = "privilege_id", referencedColumnName = "id")) + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "apikeys_privileges", + joinColumns = + @JoinColumn(name = "apikey_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "privilege_id", + referencedColumnName = "id")) private List privileges; } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java index 433de88..35e9a2d 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/authentication/ApiKeyRepository.java @@ -1,6 +1,7 @@ package eu.goldenkoopa.stockmanagementsystem.repositories.authentication; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -14,4 +15,6 @@ public interface ApiKeyRepository extends JpaRepository { List findByUser(User user); boolean existsByKey(String key); + + Optional findByKey(String key); } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java index 9b14f2d..98b7fdc 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java @@ -5,6 +5,8 @@ import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.ApiKeyRepository; import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.PrivilegeRepository; import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; +import java.time.LocalDate; +import java.time.temporal.TemporalUnit; import java.util.List; import java.util.UUID; import org.springframework.security.core.userdetails.UserDetails; @@ -39,7 +41,7 @@ public List getAllApiKeys() { } @Transactional - public ApiKey generateNewApiKey(List privilegeIds, + public ApiKey generateNewApiKey(List privilegeIds, Long expirationTime, UserDetails userDetails) { if (privilegeIds == null || privilegeIds.isEmpty()) { throw new IllegalArgumentException( @@ -58,12 +60,7 @@ public ApiKey generateNewApiKey(List privilegeIds, -> privilegeRepository.findById(id).orElseThrow( () -> new RuntimeException("privilege id not found"))) .toList()); - // privilegeIds.forEach( - // (privilegeId) - // -> apiKey.getPrivileges().add( - // privilegeRepository.findById(privilegeId) - // .orElseThrow( - // () -> new RuntimeException("privilege id not found")))); + apiKey.setExpirationDate(LocalDate.now().plusDays(expirationTime)); apiKey.setKey(generateUniqueApiKey()); ApiKey apiKeyResponse = apiKeyRepository.save(apiKey); @@ -77,4 +74,9 @@ private String generateUniqueApiKey() { } while (apiKeyRepository.existsByKey(key)); return key; } + + public ApiKey getApiKeyByKey(String string) { + return apiKeyRepository.findByKey(string).orElseThrow( + () -> new RuntimeException("api key not found")); + } } From 91639b162e55f3cc34184ffaf44421ed1f57a5e6 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Mon, 24 Mar 2025 23:48:58 +0100 Subject: [PATCH 08/13] --wip-- [skip ci] --- .../services/ApiKeyService.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java index 98b7fdc..5989041 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java @@ -6,7 +6,6 @@ import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.PrivilegeRepository; import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; import java.time.LocalDate; -import java.time.temporal.TemporalUnit; import java.util.List; import java.util.UUID; import org.springframework.security.core.userdetails.UserDetails; @@ -25,8 +24,8 @@ public class ApiKeyService { private PrivilegeRepository privilegeRepository; public ApiKeyService(ApiKeyRepository apiKeyRepository, - UserRepository userRepository, - PrivilegeRepository privilegeRepository) { + UserRepository userRepository, + PrivilegeRepository privilegeRepository) { this.apiKeyRepository = apiKeyRepository; this.userRepository = userRepository; this.privilegeRepository = privilegeRepository; @@ -42,23 +41,21 @@ public List getAllApiKeys() { @Transactional public ApiKey generateNewApiKey(List privilegeIds, Long expirationTime, - UserDetails userDetails) { + UserDetails userDetails) { if (privilegeIds == null || privilegeIds.isEmpty()) { throw new IllegalArgumentException( "Privilege IDs cannot be null or empty"); } - User user = - userRepository.findByUsername(userDetails.getUsername()) - .orElseThrow( - () -> new UsernameNotFoundException("username not found")); + User user = userRepository.findByUsername(userDetails.getUsername()) + .orElseThrow( + () -> new UsernameNotFoundException("username not found")); ApiKey apiKey = new ApiKey(); apiKey.setUser(user); apiKey.setPrivileges( privilegeIds.stream() - .map(id - -> privilegeRepository.findById(id).orElseThrow( - () -> new RuntimeException("privilege id not found"))) + .map(id -> privilegeRepository.findById(id).orElseThrow( + () -> new RuntimeException("privilege id not found: " + id))) .toList()); apiKey.setExpirationDate(LocalDate.now().plusDays(expirationTime)); apiKey.setKey(generateUniqueApiKey()); From a5d3d3b1b3d686c03d831f1d587ee6280520ff49 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Wed, 21 May 2025 01:06:09 +0200 Subject: [PATCH 09/13] --wip-- [skip ci] --- .../SetupDataLoader.java | 4 - .../controllers/v1/ApiKeyController.java | 28 +++---- .../controllers/v1/UserController.java | 50 +++++++++-- .../data/authentication/User.java | 30 ++++--- .../services/UserService.java | 84 ++++++++++++++----- 5 files changed, 137 insertions(+), 59 deletions(-) diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java index f887851..e0b180a 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/SetupDataLoader.java @@ -72,8 +72,6 @@ private void createAdminIfNotFound() { } Role adminRole = roleRepository.findByName("ROLE_ADMIN"); User user = new User(); - user.setFirstName("Test"); - user.setLastName("Test"); user.setPassword(passwordEncoder.encode("admin")); user.setUsername("admin"); user.setRoles(Arrays.asList(adminRole)); @@ -87,8 +85,6 @@ private void createUserIfNotFound() { } Role adminRole = roleRepository.findByName("ROLE_USER"); User user = new User(); - user.setFirstName("Test"); - user.setLastName("Test"); user.setPassword(passwordEncoder.encode("test")); user.setUsername("user"); user.setRoles(Arrays.asList(adminRole)); diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java index 045d9d9..24d433d 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java @@ -18,9 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -/** - * ApiKeyController - */ +/** ApiKeyController */ @RestController @RequestMapping("/api/v1/apikeys") @Secured({"ROLE_ADMIN"}) @@ -30,31 +28,29 @@ public class ApiKeyController { @GetMapping public ResponseEntity> getAllApiKeys() { - List apiKeys = apiKeyService.getAllApiKeys() - .stream() - .map(ApiKeyWithUserDto::from) - .toList(); + List apiKeys = + apiKeyService.getAllApiKeys().stream().map(ApiKeyWithUserDto::from).toList(); return new ResponseEntity<>(apiKeys, HttpStatus.OK); } @CrossOrigin @PostMapping - public ResponseEntity - generateNewApiKey(@RequestBody ApiKeyRequest apiKeyRequest, - @AuthenticationPrincipal UserDetails userDetails) { + public ResponseEntity generateNewApiKey( + @RequestBody ApiKeyRequest apiKeyRequest, @AuthenticationPrincipal UserDetails userDetails) { ApiKeyWithUserDto apiKey = - ApiKeyWithUserDto.from(apiKeyService.generateNewApiKey( - apiKeyRequest.privilegeIds(), apiKeyRequest.expirationTime(), userDetails)); + ApiKeyWithUserDto.from( + apiKeyService.generateNewApiKey( + apiKeyRequest.privilegeIds(), apiKeyRequest.expirationTime(), userDetails)); return new ResponseEntity<>(apiKey, HttpStatus.CREATED); } private record ApiKeyRequest( @NotNull(message = "Privilege IDs cannot be null") - @NotEmpty(message = "Privilege IDs cannot be empty") - List<@NotNull(message = "Privilege ID cannot be null") Long> privilegeIds, + @NotEmpty(message = "Privilege IDs cannot be empty") + List<@NotNull(message = "Privilege ID cannot be null") Long> privilegeIds, @NotNull(message = "Expiration time cannot be null") - @NotEmpty(message = "Expiration time cannot be empty") - Long expirationTime) {} + @NotEmpty(message = "Expiration time cannot be empty") + Long expirationTime) {} @Autowired public ApiKeyController(ApiKeyService apiKeyService) { diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java index 34efd6e..dc87e25 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java @@ -1,14 +1,22 @@ package eu.goldenkoopa.stockmanagementsystem.controllers.v1; +import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; +import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.UserDto; import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.UserWithApiKeyDto; import eu.goldenkoopa.stockmanagementsystem.services.UserService; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -21,16 +29,32 @@ public class UserController { @GetMapping() public List getAllUsers() { - return userService.getAllUsers() - .stream() - .map(UserWithApiKeyDto::from) - .toList(); + return userService.getAllUsers().stream().map(UserWithApiKeyDto::from).toList(); } @GetMapping("/{id}") public ResponseEntity getUser(@PathVariable Long id) { - return ResponseEntity.ok( - UserWithApiKeyDto.from(userService.getUserById(id))); + UserWithApiKeyDto user = UserWithApiKeyDto.from(userService.getUserById(id)); + if (user == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(user); + } + + @PostMapping() + public ResponseEntity createUser(@RequestBody UserPostRequest userPostRequest) { + User user = userService.createUser(userPostRequest.username(), userPostRequest.password()); + return new ResponseEntity(UserDto.from(user), HttpStatus.CREATED); + } + + @PostMapping("/password") + public ResponseEntity changePassword( + @RequestBody PasswordChangeRequest passwordChangeRequest, UserDetails userDetails) { + userService.updatePassword( + userDetails.getUsername(), + passwordChangeRequest.oldPassword(), + passwordChangeRequest.newPassword()); + return ResponseEntity.noContent().build(); } @DeleteMapping("/{id}") @@ -43,4 +67,18 @@ public ResponseEntity deleteUser(@PathVariable Long id) { public UserController(UserService userService) { this.userService = userService; } + + private record PasswordChangeRequest( + @NotNull(message = "old password cannot be null") + @NotEmpty(message = "old password cannot be empty") + String oldPassword, + @NotNull(message = "new password cannot be null") + @NotEmpty(message = "new password cannot be empty") + String newPassword) {} + + private record UserPostRequest( + @NotNull(message = "username cannot be null") @NotEmpty(message = "username cannot be empty") + String username, + @NotNull(message = "password cannot be null") @NotEmpty(message = "password cannot be empty") + String password) {} } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/User.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/User.java index d015fc1..5e5749d 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/User.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/authentication/User.java @@ -1,6 +1,14 @@ package eu.goldenkoopa.stockmanagementsystem.data.authentication; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import java.util.Collection; import lombok.Data; @@ -9,27 +17,27 @@ @Table(name = "users") public class User { - @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; - private String firstName; - private String lastName; private String username; private String password; private boolean enabled; private boolean tokenExpired; - @OneToMany(mappedBy = "user") private Collection apiKeys; + @OneToMany(mappedBy = "user") + private Collection apiKeys; @ManyToMany - @JoinTable(name = "users_roles", - joinColumns = - @JoinColumn(name = "user_id", referencedColumnName = "id"), - inverseJoinColumns = - @JoinColumn(name = "role_id", referencedColumnName = "id")) + @JoinTable( + name = "users_roles", + joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) private Collection roles; @Override public String toString() { - return id + username + firstName + lastName; + return id + username; } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java index fd81674..9902314 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/UserService.java @@ -3,39 +3,48 @@ import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; import eu.goldenkoopa.stockmanagementsystem.repositories.authentication.UserRepository; import java.util.List; +import java.util.regex.Pattern; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -/** - * UserService - */ +/** UserService */ @Service public class UserService { - @Autowired UserRepository userRepository; + private static final String PASSWORD_REGEX = + "^(?=.*[a-z])" // at least one lowercase letter + + "(?=.*[A-Z])" // at least one uppercase letter + + "(?=.*\\d)" // at least one digit + + "(?=.*[^A-Za-z\\d])" // at least one special character (any non-alphanumeric) + + ".{8,}$"; // at least 8 characters - @Autowired PasswordEncoder passwordEncoder; + private static final Pattern PASSWORD_PATTERN = Pattern.compile(PASSWORD_REGEX); + + UserRepository userRepository; + + PasswordEncoder passwordEncoder; /** * Updates the password for a given user if the current password is correct. * - * @param username The username of the user. + * @param username The username of the user. * @param currentPassword The current password of the user. - * @param newPassword The new password to be set. - * @throws Exception if the current password is incorrect or the user is not - * found. + * @param newPassword The new password to be set. + * @throws Exception if the current password is incorrect or the user is not found. */ - public void updatePassword(String username, String currentPassword, - String newPassword) throws Exception { - User user = userRepository.findByUsername(username).orElseThrow( - () -> new UsernameNotFoundException("User not found")); + public void updatePassword(String username, String currentPassword, String newPassword) { + User user = + userRepository + .findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found")); if (!passwordEncoder.matches(currentPassword, user.getPassword())) { - throw new Exception("Current password is incorrect"); + throw new IllegalArgumentException("Current password is incorrect"); } + isPasswordStrong(newPassword); user.setPassword(passwordEncoder.encode(newPassword)); userRepository.save(user); } @@ -51,8 +60,7 @@ public boolean existsByUsername(String username) { } public User getUserById(Long id) { - return userRepository.findById(id).orElseThrow( - () -> new RuntimeException("id not found")); + return userRepository.findById(id).orElseThrow(() -> new RuntimeException("id not found")); } /** @@ -60,17 +68,49 @@ public User getUserById(Long id) { * * @param user The user to save. */ - public void saveUser(User user) { userRepository.save(user); } + public void saveUser(User user) { + userRepository.save(user); + } /** * Deletes a user from the repository. * * @param id The id of the user to delete. */ - public void deleteUser(Long id) { userRepository.deleteById(id); } + public void deleteUser(Long id) { + userRepository.deleteById(id); + } - /** - * Gets all users from the repository. - */ - public List getAllUsers() { return userRepository.findAll(); } + /** Gets all users from the repository. */ + public List getAllUsers() { + return userRepository.findAll(); + } + + public User createUser(String username, String password) { + isPasswordStrong(password); + User user = new User(); + user.setPassword(passwordEncoder.encode(password)); + user.setUsername(username); + user.setEnabled(true); + User returnUser = userRepository.save(user); + return returnUser; + } + + private boolean isPasswordStrong(String password) { + if (password == null || password.isBlank()) { + throw new IllegalArgumentException("Password cannot be empty"); + } + if (!PASSWORD_PATTERN.matcher(password).matches()) { + throw new IllegalArgumentException( + "Password must be at least 8 characters long and contain at least one uppercase letter, " + + "one lowercase letter, one digit, and one special character."); + } + return true; + } + + @Autowired + public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + this.userRepository = userRepository; + } } From 6ad789fcbe9617eba41a95f2472d5139a8d6f482 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Thu, 22 May 2025 01:28:03 +0200 Subject: [PATCH 10/13] --wip-- [skip ci] --- .../controllers/v1/PublicUserController.java | 42 +++++++++++++++++++ .../controllers/v1/UserController.java | 19 --------- .../data/dto/authentication/UserDto.java | 15 +++---- .../dto/authentication/UserWithApiKeyDto.java | 15 +++---- 4 files changed, 52 insertions(+), 39 deletions(-) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PublicUserController.java diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PublicUserController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PublicUserController.java new file mode 100644 index 0000000..5dafbf5 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PublicUserController.java @@ -0,0 +1,42 @@ +package eu.goldenkoopa.stockmanagementsystem.controllers.v1; + +import eu.goldenkoopa.stockmanagementsystem.services.UserService; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/users") +public class PublicUserController { + + private UserService userService; + + @PostMapping("/password") + public ResponseEntity changePassword( + @RequestBody PasswordChangeRequest passwordChangeRequest, + @AuthenticationPrincipal UserDetails userDetails) { + userService.updatePassword( + userDetails.getUsername(), + passwordChangeRequest.oldPassword(), + passwordChangeRequest.newPassword()); + return ResponseEntity.noContent().build(); + } + + private record PasswordChangeRequest( + @NotNull(message = "old password cannot be null") + @NotEmpty(message = "old password cannot be empty") + String oldPassword, + @NotNull(message = "new password cannot be null") + @NotEmpty(message = "new password cannot be empty") + String newPassword) {} + + public PublicUserController(UserService userService) { + this.userService = userService; + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java index dc87e25..47367da 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java @@ -11,7 +11,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -47,16 +46,6 @@ public ResponseEntity createUser(@RequestBody UserPostRequest userPostR return new ResponseEntity(UserDto.from(user), HttpStatus.CREATED); } - @PostMapping("/password") - public ResponseEntity changePassword( - @RequestBody PasswordChangeRequest passwordChangeRequest, UserDetails userDetails) { - userService.updatePassword( - userDetails.getUsername(), - passwordChangeRequest.oldPassword(), - passwordChangeRequest.newPassword()); - return ResponseEntity.noContent().build(); - } - @DeleteMapping("/{id}") public ResponseEntity deleteUser(@PathVariable Long id) { userService.deleteUser(id); @@ -68,14 +57,6 @@ public UserController(UserService userService) { this.userService = userService; } - private record PasswordChangeRequest( - @NotNull(message = "old password cannot be null") - @NotEmpty(message = "old password cannot be empty") - String oldPassword, - @NotNull(message = "new password cannot be null") - @NotEmpty(message = "new password cannot be empty") - String newPassword) {} - private record UserPostRequest( @NotNull(message = "username cannot be null") @NotEmpty(message = "username cannot be empty") String username, diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java index 3f618da..08af650 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java @@ -3,24 +3,19 @@ import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; /** - * A data transfer object (DTO) representing a user. - * This record encapsulates the essential information of a user, - * including their unique identifier, first name, last name, and username. + * A data transfer object (DTO) representing a user. This record encapsulates the essential + * information of a user, including their unique identifier, first name, last name, and username. */ -public record UserDto(Long id, String firstName, String lastName, - String username) { +public record UserDto(Long id, String username) { /** * Creates a UserDto from a User entity. * * @param user The User entity to convert * @return A new UserDto instance containing the user's information - * @throws IllegalArgumentException if the user is null or if any required - * field - * is null or blank + * @throws IllegalArgumentException if the user is null or if any required field is null or blank */ public static UserDto from(User user) { - return new UserDto(user.getId(), user.getFirstName(), user.getLastName(), - user.getUsername()); + return new UserDto(user.getId(), user.getUsername()); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java index d51dfe1..a0f7fa7 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java @@ -4,25 +4,20 @@ import java.util.List; /** - * A data transfer object (DTO) representing a user with associated API keys. - * This record encapsulates user information along with a list of their API - * keys. + * A data transfer object (DTO) representing a user with associated API keys. This record + * encapsulates user information along with a list of their API keys. */ -public record UserWithApiKeyDto(Long id, String firstName, String lastName, - String username, List apiKeys) { +public record UserWithApiKeyDto(Long id, String username, List apiKeys) { /** * Creates a UserWithApiKeyDto from a User entity. * * @param user The User entity to convert - * @return A new UserWithApiKeyDto instance containing the user's information - * and API keys + * @return A new UserWithApiKeyDto instance containing the user's information and API keys * @throws NullPointerException if the user parameter is null */ public static UserWithApiKeyDto from(User user) { return new UserWithApiKeyDto( - user.getId(), user.getFirstName(), user.getLastName(), - user.getUsername(), - user.getApiKeys().stream().map(ApiKeyDto::from).toList()); + user.getId(), user.getUsername(), user.getApiKeys().stream().map(ApiKeyDto::from).toList()); } } From 462bc29ca1f30cc1e313f04e27ed94c2e1a43996 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Fri, 13 Jun 2025 23:33:04 +0200 Subject: [PATCH 11/13] --wip-- [skip ci] --- build.gradle | 1 + .../controllers/v1/ApiKeyController.java | 30 ++-- .../controllers/v1/ContainerController.java | 166 +++++++++--------- .../controllers/v1/GroupController.java | 8 +- .../controllers/v1/PrivilegeController.java | 6 +- .../controllers/v1/UserController.java | 27 ++- .../stockmanagementsystem/data/Container.java | 31 ++-- .../stockmanagementsystem/data/Group.java | 11 +- .../dto/request/ApiKeyPostRequestDTO.java | 15 ++ .../dto/request/ContainerPostRequestDTO.java | 9 + .../data/dto/request/UserPostRequestDTO.java | 11 ++ .../data/dto/response/ContainerDTO.java | 33 ++++ .../authentication/ApiKeyDTO.java} | 10 +- .../authentication/ApiKeyWithUserDTO.java} | 14 +- .../authentication/PrivilegeDTO.java} | 8 +- .../authentication/UserDTO.java} | 8 +- .../authentication/UserWithApiKeyDTO.java} | 10 +- .../repositories/ContainerRepository.java | 6 +- .../services/ApiKeyService.java | 33 ++-- .../services/ContainerService.java | 33 ++++ 20 files changed, 286 insertions(+), 184 deletions(-) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ApiKeyPostRequestDTO.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ContainerPostRequestDTO.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/UserPostRequestDTO.java create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/ContainerDTO.java rename src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/{authentication/ApiKeyDto.java => response/authentication/ApiKeyDTO.java} (72%) rename src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/{authentication/ApiKeyWithUserDto.java => response/authentication/ApiKeyWithUserDTO.java} (70%) rename src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/{authentication/PrivilegeDto.java => response/authentication/PrivilegeDTO.java} (68%) rename src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/{authentication/UserDto.java => response/authentication/UserDTO.java} (70%) rename src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/{authentication/UserWithApiKeyDto.java => response/authentication/UserWithApiKeyDTO.java} (67%) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java diff --git a/build.gradle b/build.gradle index 844ad63..bc4ef4d 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-validation:3.5.0' testImplementation 'org.springframework.security:spring-security-test' compileOnly 'org.projectlombok:lombok' runtimeOnly 'org.postgresql:postgresql' diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java index 24d433d..5efdfc8 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ApiKeyController.java @@ -1,10 +1,12 @@ package eu.goldenkoopa.stockmanagementsystem.controllers.v1; -import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.ApiKeyWithUserDto; +import eu.goldenkoopa.stockmanagementsystem.data.dto.request.ApiKeyPostRequestDTO; +import eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication.ApiKeyWithUserDTO; import eu.goldenkoopa.stockmanagementsystem.services.ApiKeyService; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; + import java.util.List; + +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -27,31 +29,23 @@ public class ApiKeyController { private final ApiKeyService apiKeyService; @GetMapping - public ResponseEntity> getAllApiKeys() { - List apiKeys = - apiKeyService.getAllApiKeys().stream().map(ApiKeyWithUserDto::from).toList(); + public ResponseEntity> getAllApiKeys() { + List apiKeys = + apiKeyService.getAllApiKeys().stream().map(ApiKeyWithUserDTO::from).toList(); return new ResponseEntity<>(apiKeys, HttpStatus.OK); } @CrossOrigin @PostMapping - public ResponseEntity generateNewApiKey( - @RequestBody ApiKeyRequest apiKeyRequest, @AuthenticationPrincipal UserDetails userDetails) { - ApiKeyWithUserDto apiKey = - ApiKeyWithUserDto.from( + public ResponseEntity generateNewApiKey( + @Valid @RequestBody ApiKeyPostRequestDTO apiKeyRequest, @AuthenticationPrincipal UserDetails userDetails) { + ApiKeyWithUserDTO apiKey = + ApiKeyWithUserDTO.from( apiKeyService.generateNewApiKey( apiKeyRequest.privilegeIds(), apiKeyRequest.expirationTime(), userDetails)); return new ResponseEntity<>(apiKey, HttpStatus.CREATED); } - private record ApiKeyRequest( - @NotNull(message = "Privilege IDs cannot be null") - @NotEmpty(message = "Privilege IDs cannot be empty") - List<@NotNull(message = "Privilege ID cannot be null") Long> privilegeIds, - @NotNull(message = "Expiration time cannot be null") - @NotEmpty(message = "Expiration time cannot be empty") - Long expirationTime) {} - @Autowired public ApiKeyController(ApiKeyService apiKeyService) { this.apiKeyService = apiKeyService; diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java index 175c159..5e89c31 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java @@ -1,39 +1,34 @@ package eu.goldenkoopa.stockmanagementsystem.controllers.v1; import eu.goldenkoopa.stockmanagementsystem.data.Container; -import eu.goldenkoopa.stockmanagementsystem.data.Group; -import eu.goldenkoopa.stockmanagementsystem.repositories.ContainerRepository; -import eu.goldenkoopa.stockmanagementsystem.repositories.GroupRepository; -import java.time.LocalDateTime; -import java.util.List; -import org.jetbrains.annotations.NotNull; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +import eu.goldenkoopa.stockmanagementsystem.data.dto.request.ContainerPostRequestDTO; +import eu.goldenkoopa.stockmanagementsystem.data.dto.response.ContainerDTO; +import eu.goldenkoopa.stockmanagementsystem.services.ContainerService; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.HttpServerErrorException; @RestController -@RequestMapping("/api/v1") +@RequestMapping("/api/v1/container") @EnableMethodSecurity(securedEnabled = true) public class ContainerController { - private final ContainerRepository containerRepository; + private final ContainerService containerService; - private final GroupRepository groupRepository; - - @Secured({ "API_HEALTH_READ" }) + @Secured({"API_HEALTH_READ"}) @GetMapping("/health") public String health() { return "alive"; @@ -41,86 +36,83 @@ public String health() { @CrossOrigin @Secured({"WRITE_PRIVILEGE", "API_CONTAINER_CREATE"}) - @PostMapping("/container") - public Container setContainerDetails(@RequestBody String data, - @NotNull @RequestParam("server") String serverName) - throws ParseException { - - JSONParser parser = new JSONParser(); - JSONObject jsonObject = (JSONObject) parser.parse(data); + @PostMapping() + public ResponseEntity setContainerDetails( + @Valid @RequestBody ContainerPostRequestDTO containerDetails, + @AuthenticationPrincipal UserDetails userDetails) { - String name = (String) jsonObject.get("name"); - String user = (String) jsonObject.get("updatedBy"); - String containerData = (String) jsonObject.get("data"); - - Container item; - List containers = containerRepository.findByNameAndServer(name, serverName); - if (containers.isEmpty()) { - item = new Container(name, user, containerData, serverName); - } else { - item = containers.get(0); - item.setData(containerData); - item.setUpdatedBy(user); - item.setUpdatedAt(LocalDateTime.now()); - } - containerRepository.save(item); - - return item; + return new ResponseEntity( + ContainerDTO.from( + this.containerService.createContainer(containerDetails, userDetails.getUsername())), + HttpStatus.CREATED); } @CrossOrigin - @GetMapping("/container") - public Container getContainerDetails(@NotNull @RequestParam("containerId") String name, - @NotNull @RequestParam("server") String server) { - List item = containerRepository.findByNameAndServer(name, server); - if (item.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST); - } - return item.get(0); - } + @Secured({"READ_PRIVILEGE", "API_CONTAINER_READ"}) + @GetMapping("/{id}") + public ResponseEntity getContainer( + @PathVariable String id, @RequestParam("server") String server) { - @CrossOrigin - @DeleteMapping("/container") - public String deleteContainerItem(@NotNull @RequestParam("containerId") String name, - @NotNull @RequestParam("server") String server) { - List containerList = containerRepository.findByNameAndServer(name, server); - if (containerList.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, - "container does not exist"); - } - Container container = containerList.get(0); - for (Group group : container.getGroups()) { - group.removeContainer(container); - groupRepository.save(group); + Container container = this.containerService.getContainer(id, server); + if (container == null) { + return ResponseEntity.notFound().build(); } - containerRepository.delete(container); - return "success"; + ContainerDTO from = ContainerDTO.from(container); + return ResponseEntity.ok(from); } - @CrossOrigin - @GetMapping("/getContainers") - public List getAllContainers( - @RequestParam(value = "server", required = false) String server) { - return server != null ? containerRepository.findByServer(server) - : containerRepository.findAll(); - } - - @GetMapping("/getContainerGroups") - public List getContainerGroups(@RequestParam("server") String server, - @RequestParam("containerId") String name) { - List containerList = containerRepository.findByNameAndServer(name, server); - if (containerList.isEmpty()) { - throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, - "container does not exist"); - } - Container container = containerList.get(0); - return container.getGroups(); - } + // @CrossOrigin + // @GetMapping("/container") + // public Container getContainerDetails( + // @NotNull @RequestParam("containerId") String name, + // @NotNull @RequestParam("server") String server) { + // List item = containerRepository.findByNameAndServer(name, server); + // if (item.isEmpty()) { + // throw new HttpServerErrorException(HttpStatus.BAD_REQUEST); + // } + // return item.get(0); + // } + // + // @CrossOrigin + // @DeleteMapping("/container") + // public String deleteContainerItem( + // @NotNull @RequestParam("containerId") String name, + // @NotNull @RequestParam("server") String server) { + // List containerList = containerRepository.findByNameAndServer(name, server); + // if (containerList.isEmpty()) { + // throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); + // } + // Container container = containerList.get(0); + // for (Group group : container.getGroups()) { + // group.removeContainer(container); + // groupRepository.save(group); + // } + // containerRepository.delete(container); + // return "success"; + // } + // + // @CrossOrigin + // @GetMapping("/getContainers") + // public List getAllContainers( + // @RequestParam(value = "server", required = false) String server) { + // return server != null + // ? containerRepository.findByServer(server) + // : containerRepository.findAll(); + // } + // + // @GetMapping("/getContainerGroups") + // public List getContainerGroups( + // @RequestParam("server") String server, @RequestParam("containerId") String name) { + // List containerList = containerRepository.findByNameAndServer(name, server); + // if (containerList.isEmpty()) { + // throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); + // } + // Container container = containerList.get(0); + // return container.getGroups(); + // } @Autowired - public ContainerController(ContainerRepository containerRepository, - GroupRepository groupRepository) { - this.containerRepository = containerRepository; - this.groupRepository = groupRepository; + public ContainerController(ContainerService containerService) { + this.containerService = containerService; } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/GroupController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/GroupController.java index 3015acb..563b9fc 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/GroupController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/GroupController.java @@ -44,13 +44,13 @@ public String createGroup(@NotNull @RequestParam("name") String name, "group does not exist"); } Group group = groupOptional.get(); - List containerOptional = + Optional containerOptional = containerRepository.findByNameAndServer(containerId, server); if (containerOptional.isEmpty()) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "id does not exist"); } - Container container = containerOptional.get(0); + Container container = containerOptional.get(); group.addContainer(container); groupRepository.save(group); return "success"; @@ -108,7 +108,7 @@ public String deleteFromGroup(@RequestParam("groupId") Integer groupId, throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "group does not exist"); } - List containerOptional = + Optional containerOptional = containerRepository.findByNameAndServer(name, server); if (containerOptional.isEmpty()) { throw new HttpServerErrorException( @@ -116,7 +116,7 @@ public String deleteFromGroup(@RequestParam("groupId") Integer groupId, "containerid does not exist (on this server?)"); } Group group = groupOptional.get(); - Container container = containerOptional.get(0); + Container container = containerOptional.get(); if (!group.getContainers().contains(container)) { throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container is not in group"); diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java index 7077e8a..1c58d5b 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/PrivilegeController.java @@ -1,6 +1,6 @@ package eu.goldenkoopa.stockmanagementsystem.controllers.v1; -import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.PrivilegeDto; +import eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication.PrivilegeDTO; import eu.goldenkoopa.stockmanagementsystem.services.PrivilegeService; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -20,10 +20,10 @@ public class PrivilegeController { private PrivilegeService privilegeService; @GetMapping - public ResponseEntity> getAllPrivileges() { + public ResponseEntity> getAllPrivileges() { return ResponseEntity.ok(privilegeService.getAllPrivileges() .stream() - .map(PrivilegeDto::from) + .map(PrivilegeDTO::from) .toList()); } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java index 47367da..4fa3b80 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/UserController.java @@ -1,12 +1,14 @@ package eu.goldenkoopa.stockmanagementsystem.controllers.v1; import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; -import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.UserDto; -import eu.goldenkoopa.stockmanagementsystem.data.dto.authentication.UserWithApiKeyDto; +import eu.goldenkoopa.stockmanagementsystem.data.dto.request.UserPostRequestDTO; +import eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication.UserDTO; +import eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication.UserWithApiKeyDTO; import eu.goldenkoopa.stockmanagementsystem.services.UserService; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; + import java.util.List; + +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -27,13 +29,13 @@ public class UserController { private UserService userService; @GetMapping() - public List getAllUsers() { - return userService.getAllUsers().stream().map(UserWithApiKeyDto::from).toList(); + public List getAllUsers() { + return userService.getAllUsers().stream().map(UserWithApiKeyDTO::from).toList(); } @GetMapping("/{id}") - public ResponseEntity getUser(@PathVariable Long id) { - UserWithApiKeyDto user = UserWithApiKeyDto.from(userService.getUserById(id)); + public ResponseEntity getUser(@PathVariable Long id) { + UserWithApiKeyDTO user = UserWithApiKeyDTO.from(userService.getUserById(id)); if (user == null) { return ResponseEntity.notFound().build(); } @@ -41,9 +43,9 @@ public ResponseEntity getUser(@PathVariable Long id) { } @PostMapping() - public ResponseEntity createUser(@RequestBody UserPostRequest userPostRequest) { + public ResponseEntity createUser(@Valid @RequestBody UserPostRequestDTO userPostRequest) { User user = userService.createUser(userPostRequest.username(), userPostRequest.password()); - return new ResponseEntity(UserDto.from(user), HttpStatus.CREATED); + return new ResponseEntity(UserDTO.from(user), HttpStatus.CREATED); } @DeleteMapping("/{id}") @@ -57,9 +59,4 @@ public UserController(UserService userService) { this.userService = userService; } - private record UserPostRequest( - @NotNull(message = "username cannot be null") @NotEmpty(message = "username cannot be empty") - String username, - @NotNull(message = "password cannot be null") @NotEmpty(message = "password cannot be empty") - String password) {} } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Container.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Container.java index 713583f..6c342b1 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Container.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Container.java @@ -3,17 +3,22 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; @Data @Entity +@Builder @AllArgsConstructor @Table(name = "Container") public class Container { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; private String name; @@ -22,21 +27,19 @@ public class Container { private LocalDateTime updatedAt; private String updatedBy; - @Column(columnDefinition = "TEXT") private String data; + @Column(columnDefinition = "TEXT") + private String data; - @JsonIgnore @ManyToMany(mappedBy = "containers") private List groups; + @JsonIgnore + @ManyToMany(mappedBy = "containers") + private List groups; private String server; - public Container() {} + @Column(columnDefinition = "TEXT") + private String notes; - public Container(String name, LocalDateTime time, String username, - String data) { - this.name = name; - this.updatedAt = time; - this.updatedBy = username; - this.data = data; - } + public Container() {} public Container(String name, String user, String data, String server) { this.server = server; @@ -46,7 +49,11 @@ public Container(String name, String user, String data, String server) { this.updatedAt = LocalDateTime.now(); this.createdAt = LocalDateTime.now(); this.createdBy = user; + this.groups = new ArrayList<>(); + this.notes = ""; } - public void removeGroup(Group group) { this.groups.remove(group); } + public void removeGroup(Group group) { + this.groups.remove(group); + } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Group.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Group.java index 07bb0d5..1f0bf69 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Group.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/Group.java @@ -11,7 +11,9 @@ @Table(name = "Groups") public class Group { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; private LocalDateTime createdAt; private String createdBy; @@ -19,9 +21,10 @@ public class Group { @JsonIgnore @ManyToMany - @JoinTable(name = "group_containers", - joinColumns = @JoinColumn(name = "group_id"), - inverseJoinColumns = @JoinColumn(name = "container_id")) + @JoinTable( + name = "group_containers", + joinColumns = @JoinColumn(name = "group_id"), + inverseJoinColumns = @JoinColumn(name = "container_id")) private List containers; public Group() {} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ApiKeyPostRequestDTO.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ApiKeyPostRequestDTO.java new file mode 100644 index 0000000..14bae69 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ApiKeyPostRequestDTO.java @@ -0,0 +1,15 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.request; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import java.util.List; + +public record ApiKeyPostRequestDTO( + @NotNull(message = "Privilege IDs cannot be null") + @NotEmpty(message = "Privilege IDs cannot be empty") + List<@NotNull(message = "Privilege ID cannot be null") Long> privilegeIds, + @NotNull(message = "Expiration time cannot be null") + @NotEmpty(message = "Expiration time cannot be empty") + Long expirationTime) { +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ContainerPostRequestDTO.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ContainerPostRequestDTO.java new file mode 100644 index 0000000..c32200c --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/ContainerPostRequestDTO.java @@ -0,0 +1,9 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.request; + +import jakarta.validation.constraints.NotNull; + +public record ContainerPostRequestDTO( + @NotNull(message = "field 'name' must not be null") String name, + @NotNull(message = "field 'data' must not be null") String data, + @NotNull(message = "field 'server' must not be null") String server) { +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/UserPostRequestDTO.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/UserPostRequestDTO.java new file mode 100644 index 0000000..b889009 --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/request/UserPostRequestDTO.java @@ -0,0 +1,11 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.request; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +public record UserPostRequestDTO( + @NotNull(message = "username cannot be null") @NotEmpty(message = "username cannot be empty") + String username, + @NotNull(message = "password cannot be null") @NotEmpty(message = "password cannot be empty") + String password) { +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/ContainerDTO.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/ContainerDTO.java new file mode 100644 index 0000000..a0cdf1f --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/ContainerDTO.java @@ -0,0 +1,33 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.response; + +import eu.goldenkoopa.stockmanagementsystem.data.Container; +import eu.goldenkoopa.stockmanagementsystem.data.Group; +import java.time.LocalDateTime; +import java.util.List; + +public record ContainerDTO( + Integer id, + String name, + String createdBy, + String updateBy, + LocalDateTime createdAt, + LocalDateTime updatedAt, + String data, + List groups, + String server, + String notes) { + + public static ContainerDTO from(Container container) { + return new ContainerDTO( + container.getId(), + container.getName(), + container.getCreatedBy(), + container.getUpdatedBy(), + container.getCreatedAt(), + container.getUpdatedAt(), + container.getData(), + container.getGroups().stream().map(Group::getId).toList(), + container.getServer(), + container.getNotes()); + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/ApiKeyDTO.java similarity index 72% rename from src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyDto.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/ApiKeyDTO.java index c698ac9..6695637 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyDto.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/ApiKeyDTO.java @@ -1,4 +1,4 @@ -package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication; import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; import java.util.List; @@ -10,7 +10,7 @@ * its unique identifier, the key itself, and a list of privileges associated * with it. */ -public record ApiKeyDto(Long id, String key, List privileges) { +public record ApiKeyDTO(Long id, String key, List privileges) { /** * Creates an ApiKeyDto from an ApiKey entity. @@ -20,9 +20,9 @@ public record ApiKeyDto(Long id, String key, List privileges) { * associated privileges * @throws NullPointerException if the apiKey parameter is null */ - public static ApiKeyDto from(ApiKey apiKey) { - return new ApiKeyDto( + public static ApiKeyDTO from(ApiKey apiKey) { + return new ApiKeyDTO( apiKey.getId(), apiKey.getKey(), - apiKey.getPrivileges().stream().map(PrivilegeDto::from).toList()); + apiKey.getPrivileges().stream().map(PrivilegeDTO::from).toList()); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyWithUserDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/ApiKeyWithUserDTO.java similarity index 70% rename from src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyWithUserDto.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/ApiKeyWithUserDTO.java index 6fc3798..e37290b 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/ApiKeyWithUserDto.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/ApiKeyWithUserDTO.java @@ -1,4 +1,4 @@ -package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication; import eu.goldenkoopa.stockmanagementsystem.data.authentication.ApiKey; import java.util.List; @@ -9,8 +9,8 @@ * information of an API key, including its unique identifier, the key itself, a * list of privileges, and the associated user. */ -public record ApiKeyWithUserDto(Long id, String key, - List privileges, UserDto user) { +public record ApiKeyWithUserDTO(Long id, String key, + List privileges, UserDTO user) { /** * Creates an ApiKeyWithUserDto from an ApiKey entity. @@ -25,10 +25,10 @@ public record ApiKeyWithUserDto(Long id, String key, * are * null */ - public static ApiKeyWithUserDto from(ApiKey apiKey) { - return new ApiKeyWithUserDto( + public static ApiKeyWithUserDTO from(ApiKey apiKey) { + return new ApiKeyWithUserDTO( apiKey.getId(), apiKey.getKey(), - apiKey.getPrivileges().stream().map(PrivilegeDto::from).toList(), - UserDto.from(apiKey.getUser())); + apiKey.getPrivileges().stream().map(PrivilegeDTO::from).toList(), + UserDTO.from(apiKey.getUser())); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/PrivilegeDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/PrivilegeDTO.java similarity index 68% rename from src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/PrivilegeDto.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/PrivilegeDTO.java index 36bb184..20ee7e5 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/PrivilegeDto.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/PrivilegeDTO.java @@ -1,4 +1,4 @@ -package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication; import eu.goldenkoopa.stockmanagementsystem.data.authentication.Privilege; @@ -7,7 +7,7 @@ * This record encapsulates the essential information of a privilege, * including its unique identifier and name. */ -public record PrivilegeDto(Long id, String name) { +public record PrivilegeDTO(Long id, String name) { /** * Creates a PrivilegeDto from a Privilege entity. @@ -16,7 +16,7 @@ public record PrivilegeDto(Long id, String name) { * @return A new PrivilegeDto instance containing the privilege's information * @throws NullPointerException if the privilege parameter is null */ - public static PrivilegeDto from(Privilege privilege) { - return new PrivilegeDto(privilege.getId(), privilege.getName()); + public static PrivilegeDTO from(Privilege privilege) { + return new PrivilegeDTO(privilege.getId(), privilege.getName()); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/UserDTO.java similarity index 70% rename from src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/UserDTO.java index 08af650..ab99218 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserDto.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/UserDTO.java @@ -1,4 +1,4 @@ -package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication; import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; @@ -6,7 +6,7 @@ * A data transfer object (DTO) representing a user. This record encapsulates the essential * information of a user, including their unique identifier, first name, last name, and username. */ -public record UserDto(Long id, String username) { +public record UserDTO(Long id, String username) { /** * Creates a UserDto from a User entity. @@ -15,7 +15,7 @@ public record UserDto(Long id, String username) { * @return A new UserDto instance containing the user's information * @throws IllegalArgumentException if the user is null or if any required field is null or blank */ - public static UserDto from(User user) { - return new UserDto(user.getId(), user.getUsername()); + public static UserDTO from(User user) { + return new UserDTO(user.getId(), user.getUsername()); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/UserWithApiKeyDTO.java similarity index 67% rename from src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java rename to src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/UserWithApiKeyDTO.java index a0f7fa7..9bcf48d 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/authentication/UserWithApiKeyDto.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/authentication/UserWithApiKeyDTO.java @@ -1,4 +1,4 @@ -package eu.goldenkoopa.stockmanagementsystem.data.dto.authentication; +package eu.goldenkoopa.stockmanagementsystem.data.dto.response.authentication; import eu.goldenkoopa.stockmanagementsystem.data.authentication.User; import java.util.List; @@ -7,7 +7,7 @@ * A data transfer object (DTO) representing a user with associated API keys. This record * encapsulates user information along with a list of their API keys. */ -public record UserWithApiKeyDto(Long id, String username, List apiKeys) { +public record UserWithApiKeyDTO(Long id, String username, List apiKeys) { /** * Creates a UserWithApiKeyDto from a User entity. @@ -16,8 +16,8 @@ public record UserWithApiKeyDto(Long id, String username, List apiKey * @return A new UserWithApiKeyDto instance containing the user's information and API keys * @throws NullPointerException if the user parameter is null */ - public static UserWithApiKeyDto from(User user) { - return new UserWithApiKeyDto( - user.getId(), user.getUsername(), user.getApiKeys().stream().map(ApiKeyDto::from).toList()); + public static UserWithApiKeyDTO from(User user) { + return new UserWithApiKeyDTO( + user.getId(), user.getUsername(), user.getApiKeys().stream().map(ApiKeyDTO::from).toList()); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java index a072c97..790da68 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java @@ -2,6 +2,8 @@ import eu.goldenkoopa.stockmanagementsystem.data.Container; import java.util.List; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -10,5 +12,7 @@ public interface ContainerRepository extends JpaRepository { List findByServer(String server); - List findByNameAndServer(String name, String server); + Optional findByNameAndServer(String name, String server); + + boolean existsByNameAndServer(String name, String server); } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java index 5989041..06ee47e 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ApiKeyService.java @@ -13,9 +13,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -/** - * ApiKeyService - */ +/** ApiKeyService */ @Service public class ApiKeyService { @@ -23,7 +21,8 @@ public class ApiKeyService { private UserRepository userRepository; private PrivilegeRepository privilegeRepository; - public ApiKeyService(ApiKeyRepository apiKeyRepository, + public ApiKeyService( + ApiKeyRepository apiKeyRepository, UserRepository userRepository, PrivilegeRepository privilegeRepository) { this.apiKeyRepository = apiKeyRepository; @@ -40,22 +39,25 @@ public List getAllApiKeys() { } @Transactional - public ApiKey generateNewApiKey(List privilegeIds, Long expirationTime, - UserDetails userDetails) { + public ApiKey generateNewApiKey( + List privilegeIds, Long expirationTime, UserDetails userDetails) { if (privilegeIds == null || privilegeIds.isEmpty()) { - throw new IllegalArgumentException( - "Privilege IDs cannot be null or empty"); + throw new IllegalArgumentException("Privilege IDs cannot be null or empty"); } - User user = userRepository.findByUsername(userDetails.getUsername()) - .orElseThrow( - () -> new UsernameNotFoundException("username not found")); + User user = + userRepository + .findByUsername(userDetails.getUsername()) + .orElseThrow(() -> new UsernameNotFoundException("username not found")); ApiKey apiKey = new ApiKey(); apiKey.setUser(user); apiKey.setPrivileges( privilegeIds.stream() - .map(id -> privilegeRepository.findById(id).orElseThrow( - () -> new RuntimeException("privilege id not found: " + id))) + .map( + id -> + privilegeRepository + .findById(id) + .orElseThrow(() -> new RuntimeException("privilege id not found: " + id))) .toList()); apiKey.setExpirationDate(LocalDate.now().plusDays(expirationTime)); apiKey.setKey(generateUniqueApiKey()); @@ -73,7 +75,8 @@ private String generateUniqueApiKey() { } public ApiKey getApiKeyByKey(String string) { - return apiKeyRepository.findByKey(string).orElseThrow( - () -> new RuntimeException("api key not found")); + return apiKeyRepository + .findByKey(string) + .orElseThrow(() -> new RuntimeException("api key not found")); } } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java new file mode 100644 index 0000000..8c45f0a --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java @@ -0,0 +1,33 @@ +package eu.goldenkoopa.stockmanagementsystem.services; + +import eu.goldenkoopa.stockmanagementsystem.data.Container; +import eu.goldenkoopa.stockmanagementsystem.data.dto.request.ContainerPostRequestDTO; +import eu.goldenkoopa.stockmanagementsystem.repositories.ContainerRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpServerErrorException; + +@Service +public class ContainerService { + + private ContainerRepository containerRepository; + + public Container createContainer(ContainerPostRequestDTO details, String user) { + if (this.containerRepository.existsByNameAndServer(details.name(), details.server())) { + throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does already exist"); + } + + Container container = new Container(details.name(), user, details.data(), details.server()); + return this.containerRepository.save(container); + } + + public Container getContainer(String name, String server) { + return this.containerRepository.findByNameAndServer(name, server).orElse(null); + } + + @Autowired + public ContainerService(ContainerRepository containerRepository) { + this.containerRepository = containerRepository; + } +} From b60d6c2a0602c2ad08aa160ce3ae76fd60af0aa8 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Sat, 14 Jun 2025 00:03:51 +0200 Subject: [PATCH 12/13] --wip-- [skip ci] --- .../controllers/v1/ContainerController.java | 34 ++++++++++--------- .../repositories/ContainerRepository.java | 2 ++ .../services/ContainerService.java | 16 +++++++++ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java index 5e89c31..b352835 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java @@ -5,6 +5,7 @@ import eu.goldenkoopa.stockmanagementsystem.data.dto.response.ContainerDTO; import eu.goldenkoopa.stockmanagementsystem.services.ContainerService; import jakarta.validation.Valid; +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -12,7 +13,7 @@ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -22,7 +23,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/v1/container") +@RequestMapping("/api/v1/containers") @EnableMethodSecurity(securedEnabled = true) public class ContainerController { @@ -34,7 +35,11 @@ public String health() { return "alive"; } - @CrossOrigin + @GetMapping() + public List getAllContainers() { + return containerService.getAllContainers().stream().map(ContainerDTO::from).toList(); + } + @Secured({"WRITE_PRIVILEGE", "API_CONTAINER_CREATE"}) @PostMapping() public ResponseEntity setContainerDetails( @@ -47,7 +52,6 @@ public ResponseEntity setContainerDetails( HttpStatus.CREATED); } - @CrossOrigin @Secured({"READ_PRIVILEGE", "API_CONTAINER_READ"}) @GetMapping("/{id}") public ResponseEntity getContainer( @@ -61,18 +65,16 @@ public ResponseEntity getContainer( return ResponseEntity.ok(from); } - // @CrossOrigin - // @GetMapping("/container") - // public Container getContainerDetails( - // @NotNull @RequestParam("containerId") String name, - // @NotNull @RequestParam("server") String server) { - // List item = containerRepository.findByNameAndServer(name, server); - // if (item.isEmpty()) { - // throw new HttpServerErrorException(HttpStatus.BAD_REQUEST); - // } - // return item.get(0); - // } - // + @Secured({"READ_PRIVILEGE", "API_CONTAINER_DELETE"}) + @DeleteMapping("/{id}") + public ResponseEntity deleteContainer( + @PathVariable String id, @RequestParam("server") String server) { + + containerService.deleteContainer(id, server); + + return ResponseEntity.noContent().build(); + } + // @CrossOrigin // @DeleteMapping("/container") // public String deleteContainerItem( diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java index 790da68..c0edf14 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/repositories/ContainerRepository.java @@ -15,4 +15,6 @@ public interface ContainerRepository extends JpaRepository { Optional findByNameAndServer(String name, String server); boolean existsByNameAndServer(String name, String server); + + void deleteByNameAndServer(String name, String server); } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java index 8c45f0a..a5d0c70 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java @@ -2,7 +2,9 @@ import eu.goldenkoopa.stockmanagementsystem.data.Container; import eu.goldenkoopa.stockmanagementsystem.data.dto.request.ContainerPostRequestDTO; +import eu.goldenkoopa.stockmanagementsystem.data.dto.response.ContainerDTO; import eu.goldenkoopa.stockmanagementsystem.repositories.ContainerRepository; +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @@ -26,8 +28,22 @@ public Container getContainer(String name, String server) { return this.containerRepository.findByNameAndServer(name, server).orElse(null); } + /** + * Deletes a container from the repository. + * + * @param name The name of the container. + * @param server The server of the container. + */ + public void deleteContainer(String name, String server) { + containerRepository.deleteByNameAndServer(name, server); + } + @Autowired public ContainerService(ContainerRepository containerRepository) { this.containerRepository = containerRepository; } + + public List getAllContainers() { + return containerRepository.findAll(); + } } From 4930db12d0c490cbdf47f4aeb431afe14c847555 Mon Sep 17 00:00:00 2001 From: GoldenKoopa <100142836+GoldenKoopa@users.noreply.github.com> Date: Mon, 16 Jun 2025 10:18:24 +0200 Subject: [PATCH 13/13] --wip-- [skip ci] --- build.gradle | 13 +++-- .../controllers/v1/ContainerController.java | 48 +++++-------------- .../data/dto/response/GroupDTO.java | 11 +++++ .../services/ContainerService.java | 15 +++++- src/main/resources/application.yml | 6 ++- .../db/changelog/db.changelog-master.yml | 3 ++ 6 files changed, 50 insertions(+), 46 deletions(-) create mode 100644 src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/GroupDTO.java create mode 100644 src/main/resources/db/changelog/db.changelog-master.yml diff --git a/build.gradle b/build.gradle index bc4ef4d..b4c7d9b 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ plugins { id 'org.springframework.boot' version '3.3.1' id 'io.spring.dependency-management' version '1.1.5' id 'com.diffplug.eclipse.apt' version '3.26.0' + id 'org.liquibase.gradle' version '3.0.1' } group = 'eu.goldenkoopa' @@ -29,7 +30,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-validation:3.5.0' testImplementation 'org.springframework.security:spring-security-test' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + runtimeOnly 'org.postgresql:postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' @@ -38,14 +42,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.3.1' implementation 'me.paulschwarz:spring-dotenv:4.0.0' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' - // implementation 'org.mapstruct:mapstruct:1.6.3' - // implementation 'org.projectlombok:lombok-mapstruct-binding:0.2.0' - // annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3' - annotationProcessor 'org.projectlombok:lombok' - // implementation 'org.flywaydb:flyway-core:11.3.3' - // implementation 'org.flywaydb:flyway-database-postgresql' - // testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3' + // implementation 'org.liquibase:liquibase-core:4.32.0' + // runtimeOnly 'org.liquibase.ext:liquibase-hibernate6:4.32.0' } diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java index b352835..6dc7fea 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/controllers/v1/ContainerController.java @@ -1,8 +1,10 @@ package eu.goldenkoopa.stockmanagementsystem.controllers.v1; import eu.goldenkoopa.stockmanagementsystem.data.Container; +import eu.goldenkoopa.stockmanagementsystem.data.Group; import eu.goldenkoopa.stockmanagementsystem.data.dto.request.ContainerPostRequestDTO; import eu.goldenkoopa.stockmanagementsystem.data.dto.response.ContainerDTO; +import eu.goldenkoopa.stockmanagementsystem.data.dto.response.GroupDTO; import eu.goldenkoopa.stockmanagementsystem.services.ContainerService; import jakarta.validation.Valid; import java.util.List; @@ -75,43 +77,15 @@ public ResponseEntity deleteContainer( return ResponseEntity.noContent().build(); } - // @CrossOrigin - // @DeleteMapping("/container") - // public String deleteContainerItem( - // @NotNull @RequestParam("containerId") String name, - // @NotNull @RequestParam("server") String server) { - // List containerList = containerRepository.findByNameAndServer(name, server); - // if (containerList.isEmpty()) { - // throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); - // } - // Container container = containerList.get(0); - // for (Group group : container.getGroups()) { - // group.removeContainer(container); - // groupRepository.save(group); - // } - // containerRepository.delete(container); - // return "success"; - // } - // - // @CrossOrigin - // @GetMapping("/getContainers") - // public List getAllContainers( - // @RequestParam(value = "server", required = false) String server) { - // return server != null - // ? containerRepository.findByServer(server) - // : containerRepository.findAll(); - // } - // - // @GetMapping("/getContainerGroups") - // public List getContainerGroups( - // @RequestParam("server") String server, @RequestParam("containerId") String name) { - // List containerList = containerRepository.findByNameAndServer(name, server); - // if (containerList.isEmpty()) { - // throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "container does not exist"); - // } - // Container container = containerList.get(0); - // return container.getGroups(); - // } + @GetMapping("/{id}/groups") + public List getContainerGroups( + @PathVariable String id, @RequestParam("server") String server) { + + List groups = containerService.getContainerGroups(id, server); + + return groups.stream().map(GroupDTO::from).toList(); + } + @Autowired public ContainerController(ContainerService containerService) { diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/GroupDTO.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/GroupDTO.java new file mode 100644 index 0000000..6fee86d --- /dev/null +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/data/dto/response/GroupDTO.java @@ -0,0 +1,11 @@ +package eu.goldenkoopa.stockmanagementsystem.data.dto.response; + +import eu.goldenkoopa.stockmanagementsystem.data.Group; +import java.time.LocalDateTime; + +public record GroupDTO(Integer id, LocalDateTime createdAt, String createdBy, String name) { + + public static GroupDTO from(Group group) { + return new GroupDTO(group.getId(), group.getCreatedAt(), group.getCreatedBy(), group.getName()); + } +} diff --git a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java index a5d0c70..0c59b44 100644 --- a/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java +++ b/src/main/java/eu/goldenkoopa/stockmanagementsystem/services/ContainerService.java @@ -1,10 +1,12 @@ package eu.goldenkoopa.stockmanagementsystem.services; import eu.goldenkoopa.stockmanagementsystem.data.Container; +import eu.goldenkoopa.stockmanagementsystem.data.Group; import eu.goldenkoopa.stockmanagementsystem.data.dto.request.ContainerPostRequestDTO; -import eu.goldenkoopa.stockmanagementsystem.data.dto.response.ContainerDTO; import eu.goldenkoopa.stockmanagementsystem.repositories.ContainerRepository; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @@ -46,4 +48,15 @@ public ContainerService(ContainerRepository containerRepository) { public List getAllContainers() { return containerRepository.findAll(); } + + public List getContainerGroups(String name, String server) { + + Optional container = containerRepository.findByNameAndServer(name, server); + + if (container.isEmpty()) { + return new ArrayList(); + } + + return container.get().getGroups(); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ca85b8c..e2a342b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,10 +8,14 @@ spring: password: ${POSTGRES_PASSWORD} jpa: hibernate: - ddl-auto: update + ddl-auto: none properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect + liquibase: + enabled: true + change-log: classpath:/db/changelog/db.changelog-master.yml + server: error: include-message: ALWAYS diff --git a/src/main/resources/db/changelog/db.changelog-master.yml b/src/main/resources/db/changelog/db.changelog-master.yml new file mode 100644 index 0000000..172cddd --- /dev/null +++ b/src/main/resources/db/changelog/db.changelog-master.yml @@ -0,0 +1,3 @@ +databaseChangeLog: + - include: + file: db/changelog/changelog-1.yaml