diff --git a/pom.xml b/pom.xml index 63c40ad..39f36dd 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,11 @@ + + com.h2database + h2 + runtime + org.springframework.boot spring-boot-starter-freemarker @@ -33,7 +38,10 @@ org.springframework.boot spring-boot-starter-security - + + org.springframework.boot + spring-boot-starter-data-jpa + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/ru/amm/fileexplorer/server/ServerApplication.java b/src/main/java/ru/amm/fileexplorer/server/ServerApplication.java index 82b8a5f..47c1756 100644 --- a/src/main/java/ru/amm/fileexplorer/server/ServerApplication.java +++ b/src/main/java/ru/amm/fileexplorer/server/ServerApplication.java @@ -3,10 +3,17 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import java.sql.*; +import java.util.logging.Level; +import java.util.logging.Logger; + @SpringBootApplication public class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); + + + } } diff --git a/src/main/java/ru/amm/fileexplorer/server/config/SecurityConfig.java b/src/main/java/ru/amm/fileexplorer/server/config/SecurityConfig.java index 664d3ef..7f69f9f 100644 --- a/src/main/java/ru/amm/fileexplorer/server/config/SecurityConfig.java +++ b/src/main/java/ru/amm/fileexplorer/server/config/SecurityConfig.java @@ -1,37 +1,66 @@ package ru.amm.fileexplorer.server.config; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; +import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; +import ru.amm.fileexplorer.server.service.UserDetailsServiceImpl; + +import javax.sql.DataSource; @Configuration +@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.inMemoryAuthentication() - .withUser("user") - .password(passwordEncoder() - .encode("test-password")) - .roles("ADMIN"); + + @Autowired + private UserDetailsServiceImpl userDetailsService; + + @Autowired + private DataSource dataSource; + + @Bean + public BCryptPasswordEncoder passwordEncoder() { + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + return bCryptPasswordEncoder; + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + + + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + } @Override protected void configure(HttpSecurity http) throws Exception { - http.httpBasic() - .and() - .authorizeRequests() - .antMatchers("/css/**", "/scripts/**") - .permitAll() - .antMatchers("/**").not().anonymous(); - } - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); + http.authorizeRequests().antMatchers( "/login", "/logout").permitAll(); + + http.authorizeRequests().antMatchers("/").access("hasRole('ROLE_USER')"); + + http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403"); + + http.authorizeRequests().and().formLogin() + .loginProcessingUrl("/j_spring_security_check") + .loginPage("/login") + .defaultSuccessUrl("/") + .failureUrl("/login?error=true") + .usernameParameter("username") + .passwordParameter("password") + .and().logout().logoutUrl("/logout").logoutSuccessUrl("/login"); + + + } + + } diff --git a/src/main/java/ru/amm/fileexplorer/server/config/entity/AppRole.java b/src/main/java/ru/amm/fileexplorer/server/config/entity/AppRole.java new file mode 100644 index 0000000..49598b4 --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/config/entity/AppRole.java @@ -0,0 +1,35 @@ +package ru.amm.fileexplorer.server.config.entity; + +import javax.persistence.*; + +@Entity +@Table(name = "App_Role", // + uniqueConstraints = { // + @UniqueConstraint(name = "APP_ROLE_UK", columnNames = "Role_Name")}) +public final class AppRole { + + @Id + @GeneratedValue + @Column(name = "Role_Id", nullable = false) + private Long roleId; + + @Column(name = "Role_Name", length = 30, nullable = false) + private String roleName; + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/amm/fileexplorer/server/config/entity/AppUser.java b/src/main/java/ru/amm/fileexplorer/server/config/entity/AppUser.java new file mode 100644 index 0000000..d584587 --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/config/entity/AppUser.java @@ -0,0 +1,57 @@ +package ru.amm.fileexplorer.server.config.entity; + +import javax.persistence.*; + +@Entity +@Table(name = "App_User", // + uniqueConstraints = { // + @UniqueConstraint(name = "APP_USER_UK", columnNames = "User_Name")}) +public class AppUser { + + @Id + @GeneratedValue + @Column(name = "User_Id", nullable = false) + private Long userId; + + @Column(name = "User_Name", length = 36, nullable = false) + private String userName; + + @Column(name = "Encryted_Password", length = 128, nullable = false) + private String encrytedPassword; + + @Column(name = "Enabled", length = 1, nullable = false) + private boolean enabled; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getEncrytedPassword() { + return encrytedPassword; + } + + public void setEncrytedPassword(String encrytedPassword) { + this.encrytedPassword = encrytedPassword; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/amm/fileexplorer/server/config/entity/UserRole.java b/src/main/java/ru/amm/fileexplorer/server/config/entity/UserRole.java new file mode 100644 index 0000000..3a4b172 --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/config/entity/UserRole.java @@ -0,0 +1,57 @@ +package ru.amm.fileexplorer.server.config.entity; + + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +@Entity +@Table(name = "User_Role", // + uniqueConstraints = { // + @UniqueConstraint(name = "USER_ROLE_UK", columnNames = { "User_Id", "Role_Id" }) }) +public class UserRole { + + @Id + @GeneratedValue + @Column(name = "Id", nullable = false) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "User_Id", nullable = false) + private AppUser appUser; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "Role_Id", nullable = false) + private AppRole appRole; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public AppUser getAppUser() { + return appUser; + } + + public void setAppUser(AppUser appUser) { + this.appUser = appUser; + } + + public AppRole getAppRole() { + return appRole; + } + + public void setAppRole(AppRole appRole) { + this.appRole = appRole; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/amm/fileexplorer/server/controller/ErrorPageController.java b/src/main/java/ru/amm/fileexplorer/server/controller/ErrorPageController.java index 4230d3c..57f451c 100644 --- a/src/main/java/ru/amm/fileexplorer/server/controller/ErrorPageController.java +++ b/src/main/java/ru/amm/fileexplorer/server/controller/ErrorPageController.java @@ -13,16 +13,16 @@ public class ErrorPageController implements ErrorController { @RequestMapping(method = RequestMethod.GET) public ModelAndView processError(HttpServletRequest req) { - int status = getErrorCode(req); + int status = getErrorCode( req ); String message = "Unknown error"; - if (status == 404) { + if ( status == 404 ) { message = "The requested page not found"; } - return new ModelAndView("error-page", "errorMessage", message); + return new ModelAndView( "error-page", "errorMessage", message ); } private int getErrorCode(HttpServletRequest httpRequest) { - return (Integer) httpRequest.getAttribute("javax.servlet.error.status_code"); + return (Integer) httpRequest.getAttribute( "javax.servlet.error.status_code" ); } @Override diff --git a/src/main/java/ru/amm/fileexplorer/server/controller/IndexController.java b/src/main/java/ru/amm/fileexplorer/server/controller/IndexController.java index b783c1b..efddf12 100644 --- a/src/main/java/ru/amm/fileexplorer/server/controller/IndexController.java +++ b/src/main/java/ru/amm/fileexplorer/server/controller/IndexController.java @@ -1,7 +1,6 @@ package ru.amm.fileexplorer.server.controller; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.http.ContentDisposition; @@ -21,14 +20,15 @@ import ru.amm.fileexplorer.server.data.FileType; import ru.amm.fileexplorer.server.data.NamePartialMatcher; import ru.amm.fileexplorer.server.service.FileExplorerService; -import ru.amm.fileexplorer.server.service.FileSystemProvider; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Controller public class IndexController { @@ -38,55 +38,62 @@ public class IndexController { @RequestMapping(path = "/", method = RequestMethod.GET) public ModelAndView index(@RequestParam(name = "path", required = false) String path) { DirectoryContents dirContents; - if (path == null) { - dirContents = explorerService.getRootContents(); + System.out.println( path ); + if ( path == null || "/".equals( path ) ) { + dirContents = explorerService.getRootContents( ); } else { - dirContents = explorerService.getContents(path); + dirContents = explorerService.getContents( path ); } - Map data = new HashMap<>(); - data.put("directory", dirContents); - return new ModelAndView("index", data); + Map data = new HashMap<>( ); + data.put( "directory", dirContents ); + return new ModelAndView( "index", data ); } @RequestMapping(path = "/search", method = RequestMethod.GET) public ModelAndView search(@RequestParam(name = "search") String search, @RequestParam(name = "path") String path) { DirectoryContents dirContents; - dirContents = explorerService.getContentsFiltered(path, new NamePartialMatcher(search)); - Map data = new HashMap<>(); - data.put("directory", dirContents); - return new ModelAndView("filesView", data); + dirContents = explorerService.getContentsFiltered( path, new NamePartialMatcher( search ) ); + Map data = new HashMap<>( ); + data.put( "directory", dirContents ); + return new ModelAndView( "filesView", data ); } + @RequestMapping(value = "/login", method = RequestMethod.GET) + public String loginPage(Model model) { + return "loginPage"; + } + + @RequestMapping(path = "/searchAll", method = RequestMethod.GET) public ModelAndView searchAll(@RequestParam(name = "search") String search, @RequestParam(name = "path") String path) { DirectoryContents dirContents; - if (!search.equals("")) - dirContents = explorerService.getContentsFilteredAll(path, new NamePartialMatcher(search)); - else dirContents = explorerService.getContents(path); - Map data = new HashMap<>(); - data.put("directory", dirContents); - return new ModelAndView("filesView", data); + if ( !search.equals( "" ) ) + dirContents = explorerService.getContentsFilteredAll( path, new NamePartialMatcher( search ) ); + else dirContents = explorerService.getContents( path ); + Map data = new HashMap<>( ); + data.put( "directory", dirContents ); + return new ModelAndView( "filesView", data ); } @RequestMapping(path = "/download", method = RequestMethod.GET) public ResponseEntity downloadFile(@RequestParam(name = "file") String file) { - FileData f = explorerService.getFile(file); - InputStreamResource fStream = new InputStreamResource(explorerService.getFileStream(f)); - String fName = f.getName(); - FileType fType = f.getFileType(); - if (f.isDirectory()) { + FileData f = explorerService.getFile( file ); + InputStreamResource fStream = new InputStreamResource( explorerService.getFileStream( f ) ); + String fName = f.getName( ); + FileType fType = f.getFileType( ); + if ( f.isDirectory( ) ) { fName += ".zip"; - f.getFileType().setExtension("zip"); + f.getFileType( ).setExtension( "zip" ); } - ContentDisposition content = ContentDisposition.builder("attachment") - .filename(fName, StandardCharsets.UTF_8) - .build(); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, content.toString()) - .contentType(new MediaType(fType.getBase(), fType.getExtension())) - .body(fStream); + ContentDisposition content = ContentDisposition.builder( "attachment" ) + .filename( fName, StandardCharsets.UTF_8 ) + .build( ); + return ResponseEntity.ok( ) + .header( HttpHeaders.CONTENT_DISPOSITION, content.toString( ) ) + .contentType( new MediaType( fType.getBase( ), fType.getExtension( ) ) ) + .body( fStream ); } @@ -96,20 +103,41 @@ public String uploadingPost( @RequestParam(name = "path", required = false) Path path, RedirectAttributes attributes) throws IOException { - Path destPath = explorerService.getAbsolutePath(path); + Path destPath; + if ( path == null ) + destPath = explorerService.getRootPath( ); + else + destPath = explorerService.getAbsolutePath( path ); + for (MultipartFile uploadedFile : uploadingFiles) { - File file = destPath.resolve(uploadedFile.getOriginalFilename()).toFile(); - uploadedFile.transferTo(file); + File file = destPath.resolve( uploadedFile.getOriginalFilename( ) ).toFile( ); + uploadedFile.transferTo( file ); } - // preserve ?path= GET parameter after the redirect - attributes.addAttribute("path", path.toString()); + + + attributes.addAttribute( "path", String.valueOf( path ) ); + return "redirect:/"; + } + + @RequestMapping(value = "/createdir", method = RequestMethod.POST) + public String createDir( + @RequestParam("directory") String directory, + @RequestParam(name = "path", required = false) Optional oPath, + RedirectAttributes attributes) + throws IOException { + String directoryPath; + String path = oPath.orElse( "" ); + if ( path == null ) directoryPath = explorerService.getRootPath( ).resolve( directory ).toString( ); + else directoryPath = explorerService.getAbsolutePath( Paths.get( path ) ).resolve( directory ).toString( ); + new File( directoryPath ).mkdir( ); + attributes.addAttribute( "path", path ); return "redirect:/"; } @RequestMapping(path = "/testcss", method = RequestMethod.GET) public ModelAndView testcss() { - Map data = new HashMap<>(); - data.put("WhatTest", "CSS"); - return new ModelAndView("testcss", data); + Map data = new HashMap<>( ); + data.put( "WhatTest", "CSS" ); + return new ModelAndView( "testcss", data ); } } diff --git a/src/main/java/ru/amm/fileexplorer/server/dao/AppRoleDAO.java b/src/main/java/ru/amm/fileexplorer/server/dao/AppRoleDAO.java new file mode 100644 index 0000000..b24a3bd --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/dao/AppRoleDAO.java @@ -0,0 +1,28 @@ +package ru.amm.fileexplorer.server.dao; + + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.amm.fileexplorer.server.config.entity.UserRole; + +@Repository +@Transactional +public class AppRoleDAO { + + @Autowired + private EntityManager entityManager; + + public List getRoleNames(Long userId) { + String sql = "Select ur.appRole.roleName from " + UserRole.class.getName() + " ur " // + + " where ur.appUser.userId = :userId "; + + Query query = this.entityManager.createQuery(sql, String.class); + query.setParameter("userId", userId); + return query.getResultList(); + }} diff --git a/src/main/java/ru/amm/fileexplorer/server/dao/AppUserDAO.java b/src/main/java/ru/amm/fileexplorer/server/dao/AppUserDAO.java new file mode 100644 index 0000000..52d8c15 --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/dao/AppUserDAO.java @@ -0,0 +1,34 @@ +package ru.amm.fileexplorer.server.dao; + + +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.Query; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import ru.amm.fileexplorer.server.config.entity.AppUser; + +@Repository +@Transactional +public class AppUserDAO { + + @Autowired + private EntityManager entityManager; + + public AppUser findUserAccount(String userName) { + try { + String sql = "Select e from " + AppUser.class.getName() + " e " // + + " Where e.userName = :userName "; + + Query query = entityManager.createQuery(sql, AppUser.class); + query.setParameter("userName", userName); + + return (AppUser) query.getSingleResult(); + } catch (NoResultException e) { + return null; + } + } + +} \ No newline at end of file diff --git a/src/main/java/ru/amm/fileexplorer/server/service/FileExplorerService.java b/src/main/java/ru/amm/fileexplorer/server/service/FileExplorerService.java index a3a0904..20c53a9 100644 --- a/src/main/java/ru/amm/fileexplorer/server/service/FileExplorerService.java +++ b/src/main/java/ru/amm/fileexplorer/server/service/FileExplorerService.java @@ -8,87 +8,93 @@ import java.io.InputStream; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import java.util.stream.Collectors; @Service public class FileExplorerService { - public static final NoOpMatcher NO_OP_MATCHER = new NoOpMatcher(); + public static final NoOpMatcher NO_OP_MATCHER = new NoOpMatcher( ); + private final static Logger LOG = LogManager.getLogger( FileExplorerService.class ); @Autowired private FileSystemProvider provider; - private final static Logger LOG = LogManager.getLogger(FileExplorerService.class); - public DirectoryContents getRootContents() { - DirectoryContents contents = getDirectoryContents("", NO_OP_MATCHER); + DirectoryContents contents = getDirectoryContents( "", NO_OP_MATCHER ); return contents; } public Path getAbsolutePath(Path relativePath) { - return provider.getAbsolutePath(relativePath); + return provider.getAbsolutePath( relativePath ); + } + + public Path getRootPath() { + return provider.getPathToPublish( ); } public DirectoryContents getContentsFiltered(String relativePath, FileMatcher matcher) { - return getDirectoryContents(relativePath, matcher); + return getDirectoryContents( relativePath, matcher ); } public DirectoryContents getContentsFilteredAll(String relativePath, FileMatcher matcher) { - ArrayList content = new ArrayList<>(); - ArrayDeque directories = getContents(relativePath).getFiles().stream() - .filter(FileData::isDirectory).collect(Collectors.toCollection(ArrayDeque::new)); - while (!directories.isEmpty()) { - FileData f = directories.poll(); - List dirFiles = getContents(f.getRelativePath()).getFiles(); - directories.addAll(dirFiles.stream() - .filter(FileData::isDirectory) - .collect(Collectors.toList())); - content.addAll(dirFiles.stream().filter(matcher::matches).collect(Collectors.toList())); + ArrayList content = new ArrayList<>( ); + ArrayDeque directories = getContents( relativePath ).getFiles( ).stream( ) + .filter( FileData::isDirectory ).collect( Collectors.toCollection( ArrayDeque::new ) ); + while ( !directories.isEmpty( ) ) { + FileData f = directories.poll( ); + List dirFiles = getContents( f.getRelativePath( ) ).getFiles( ); + directories.addAll( dirFiles.stream( ) + .filter( FileData::isDirectory ) + .collect( Collectors.toList( ) ) ); + content.addAll( dirFiles.stream( ).filter( matcher::matches ).collect( Collectors.toList( ) ) ); } - return getDirectoryContents("", content, ""); + return getDirectoryContents( "", content, "" ); } public DirectoryContents getContents(String relativePath) { - return getDirectoryContents(relativePath, NO_OP_MATCHER); + return getDirectoryContents( relativePath, NO_OP_MATCHER ); } private DirectoryContents getDirectoryContents(String relativePath, FileMatcher matcher) { - List list = provider.fillFileList(relativePath); - List filteredList = list.stream() - .filter(t -> matcher.matches(t)) - .collect(Collectors.toList()); - String parentDir = provider.getParent(relativePath); + List list = provider.fillFileList( relativePath ); + List filteredList = list.stream( ) + .filter( t -> matcher.matches( t ) ) + .collect( Collectors.toList( ) ); + String parentDir = provider.getParent( relativePath ); - return FileExplorerService.this.getDirectoryContents(relativePath, filteredList, parentDir); + return FileExplorerService.this.getDirectoryContents( relativePath, filteredList, parentDir ); } private DirectoryContents getDirectoryContents(String relativePath, List filteredList, String parentDir) { - DirectoryContents dc = new DirectoryContents(relativePath, parentDir, filteredList); - dc.getParentFolders().addAll(getParentFolders(relativePath)); + DirectoryContents dc = new DirectoryContents( relativePath, parentDir, filteredList ); + dc.getParentFolders( ).addAll( getParentFolders( relativePath ) ); return dc; } private List getParentFolders(String relativePath) { - LinkedList parents = new LinkedList<>(); - Path path = Path.of(relativePath); + LinkedList parents = new LinkedList<>( ); + Path path = Path.of( relativePath ); - while (path != null && path.getNameCount() > 0) { - parents.addFirst(new ParentFolder(path.getFileName().toString(), - path.toString())); - path = path.getParent(); + while ( path != null && path.getNameCount( ) > 0 ) { + parents.addFirst( new ParentFolder( path.getFileName( ).toString( ), + path.toString( ) ) ); + path = path.getParent( ); } - parents.addFirst(new ParentFolder("/", "/")); + parents.addFirst( new ParentFolder( "/", "/" ) ); return parents; } public InputStream getFileStream(FileData f) { - if (f.isDirectory()) { - return provider.getDirectoryStream(f.getRelativePath()); + if ( f.isDirectory( ) ) { + return provider.getDirectoryStream( f.getRelativePath( ) ); } - return provider.getFileStream(f.getRelativePath()); + return provider.getFileStream( f.getRelativePath( ) ); } public FileData getFile(String relativePath) { - return provider.getFile(relativePath); + return provider.getFile( relativePath ); } } diff --git a/src/main/java/ru/amm/fileexplorer/server/service/FileSystemProvider.java b/src/main/java/ru/amm/fileexplorer/server/service/FileSystemProvider.java index 6ce077f..04e0194 100644 --- a/src/main/java/ru/amm/fileexplorer/server/service/FileSystemProvider.java +++ b/src/main/java/ru/amm/fileexplorer/server/service/FileSystemProvider.java @@ -23,96 +23,101 @@ public class FileSystemProvider { private Path pathToPublish; public List fillFileList(String relpath) { - List result = new ArrayList<>(); + List result = new ArrayList<>( ); - Path absPath = pathToPublish.resolve(relpath); - try (DirectoryStream stream = Files.newDirectoryStream(absPath)) { + Path absPath = pathToPublish.resolve( relpath ); + try ( DirectoryStream stream = Files.newDirectoryStream( absPath ) ) { for (Path file : stream) { - BasicFileAttributes attr = Files.readAttributes(file, BasicFileAttributes.class); - FileData fileData = toFileData(file, attr, relpath, Files.probeContentType(file)); - result.add(fileData); + BasicFileAttributes attr = Files.readAttributes( file, BasicFileAttributes.class ); + FileData fileData = toFileData( file, attr, relpath, Files.probeContentType( file ) ); + result.add( fileData ); } - } catch (IOException | DirectoryIteratorException e) { - throw new DirectoryAccessException(e); + } catch ( IOException | DirectoryIteratorException e ) { + throw new DirectoryAccessException( e ); } return result; } + public Path getPathToPublish() { + return pathToPublish; + } + public Path getAbsolutePath(Path relPath) { - return pathToPublish.resolve(relPath); + return pathToPublish.resolve( relPath ); } private FileData toFileData(Path file, BasicFileAttributes attr, String relpath, String mimeType) { - FileData fileData = new FileData(); - String name = file.getFileName().toString(); - fileData.setName(name); - fileData.setDirectory(attr.isDirectory()); - fileData.setSize(attr.size()); - fileData.setLastModifiedTime(new Date(attr.lastModifiedTime().toMillis())); - if (mimeType == null) { - fileData.setFileType(FileType.get()); + FileData fileData = new FileData( ); + String name = file.getFileName( ).toString( ); + fileData.setName( name ); + fileData.setDirectory( attr.isDirectory( ) ); + fileData.setSize( attr.size( ) ); + fileData.setLastModifiedTime( new Date( attr.lastModifiedTime( ).toMillis( ) ) ); + if ( mimeType == null ) { + fileData.setFileType( FileType.get( ) ); } else { - String[] m = mimeType.split("/"); - fileData.setFileType(FileType.get(m[0], m[1])); + String[] m = mimeType.split( "/" ); + fileData.setFileType( FileType.get( m[0], m[1] ) ); } - fileData.setRelativePath(Path.of(relpath, name).toString()); + fileData.setRelativePath( Path.of( relpath, name ).toString( ) ); return fileData; } public String getParent(String relativePath) { - Optional path = ofNullable(Path.of(relativePath)) - .map(Path::getParent) - .map(Path::toString); - return path.orElse(""); + Optional path = ofNullable( Path.of( relativePath ) ) + .map( Path::getParent ) + .map( Path::toString ); + return path.orElse( "" ); } public InputStream getFileStream(String relativePath) { try { - Path absPath = pathToPublish.resolve(relativePath); - return new FileInputStream(absPath.toFile()); - } catch (FileNotFoundException e) { - throw new DirectoryAccessException(e); + Path absPath = pathToPublish.resolve( relativePath ); + return new FileInputStream( absPath.toFile( ) ); + } catch ( FileNotFoundException e ) { + throw new DirectoryAccessException( e ); } } public InputStream getDirectoryStream(String relativePath) { try { - Path f = pathToPublish.resolve(relativePath); - Path tempZip = Files.createTempFile("tmpZip", null); - try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tempZip.toFile()))) { - Files.walkFileTree(f, new SimpleFileVisitor() { + Path f = pathToPublish.resolve( relativePath ); + Path tempZip = Files.createTempFile( "tmpZip", null ); + try ( ZipOutputStream zos = new ZipOutputStream( new FileOutputStream( tempZip.toFile( ) ) ) ) { + Files.walkFileTree( f, new SimpleFileVisitor( ) { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - zos.putNextEntry(new ZipEntry(f.relativize(file).toString())); - Files.copy(file, zos); - zos.closeEntry(); + zos.putNextEntry( new ZipEntry( f.relativize( file ).toString( ) ) ); + Files.copy( file, zos ); + zos.closeEntry( ); return FileVisitResult.CONTINUE; } - }); - zos.close(); - return new FileInputStream(tempZip.toFile()); - } catch (IOException ex) { - throw new DirectoryAccessException(ex); + } ); + zos.close( ); + return new FileInputStream( tempZip.toFile( ) ); + } catch ( IOException ex ) { + throw new DirectoryAccessException( ex ); } - } catch (IOException e) { - throw new DirectoryAccessException(e); + } catch ( IOException e ) { + throw new DirectoryAccessException( e ); } } public FileData getFile(String relativePath) { try { - Path f = pathToPublish.resolve(relativePath); - BasicFileAttributes attr = Files.readAttributes(f, BasicFileAttributes.class); - return toFileData(f, attr, relativePath.replace(f.getFileName().toString(), ""), Files.probeContentType(f)); - } catch (IOException e) { - throw new DirectoryAccessException(e); + Path f = pathToPublish.resolve( relativePath ); + BasicFileAttributes attr = Files.readAttributes( f, BasicFileAttributes.class ); + return toFileData( f, attr, relativePath.replace( f.getFileName( ).toString( ), "" ), Files.probeContentType( f ) ); + } catch ( IOException e ) { + throw new DirectoryAccessException( e ); } } - public String getPathOfFolder(String path,String pathToPublish) { - if (path == null) { + + public String getPathOfFolder(String path, String pathToPublish) { + if ( path == null ) { path = ""; } - Path p = Paths.get(pathToPublish, path); - return p.toString(); + Path p = Paths.get( pathToPublish, path ); + return p.toString( ); } } diff --git a/src/main/java/ru/amm/fileexplorer/server/service/UserDetailsServiceImpl.java b/src/main/java/ru/amm/fileexplorer/server/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..24a0b47 --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/service/UserDetailsServiceImpl.java @@ -0,0 +1,55 @@ +package ru.amm.fileexplorer.server.service; + + +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.User; +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 ru.amm.fileexplorer.server.config.entity.AppUser; +import ru.amm.fileexplorer.server.dao.AppRoleDAO; +import ru.amm.fileexplorer.server.dao.AppUserDAO; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Autowired + private AppUserDAO appUserDAO; + + @Autowired + private AppRoleDAO appRoleDAO; + + @Override + public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { + AppUser appUser = this.appUserDAO.findUserAccount( userName ); + + if ( appUser == null ) { + System.out.println( "User not found! " + userName ); + throw new UsernameNotFoundException( "User " + userName + " was not found in the database" ); + } + + System.out.println( "Found User: " + appUser ); + + List roleNames = this.appRoleDAO.getRoleNames( appUser.getUserId( ) ); + + List grantList = new ArrayList( ); + if ( roleNames != null ) { + for (String role : roleNames) { + GrantedAuthority authority = new SimpleGrantedAuthority( role ); + grantList.add( authority ); + } + } + + UserDetails userDetails = (UserDetails) new User( appUser.getUserName( ), + appUser.getEncrytedPassword( ), grantList ); + + return userDetails; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/amm/fileexplorer/server/utils/EncrytedPasswordUtils.java b/src/main/java/ru/amm/fileexplorer/server/utils/EncrytedPasswordUtils.java new file mode 100644 index 0000000..1025719 --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/utils/EncrytedPasswordUtils.java @@ -0,0 +1,21 @@ +package ru.amm.fileexplorer.server.utils; + + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class EncrytedPasswordUtils { + + // Encryte Password with BCryptPasswordEncoder + public static String encrytePassword(String password) { + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder( ); + return encoder.encode( password ); + } + + public static void main(String[] args) { + String password = "123"; + String encrytedPassword = encrytePassword( password ); + + System.out.println( "Encryted Password: " + encrytedPassword ); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/amm/fileexplorer/server/utils/WebUtils.java b/src/main/java/ru/amm/fileexplorer/server/utils/WebUtils.java new file mode 100644 index 0000000..003f474 --- /dev/null +++ b/src/main/java/ru/amm/fileexplorer/server/utils/WebUtils.java @@ -0,0 +1,32 @@ +package ru.amm.fileexplorer.server.utils; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import java.util.Collection; + +public class WebUtils { + + public static String toString(User user) { + StringBuilder sb = new StringBuilder( ); + + sb.append( "UserName:" ).append( user.getUsername( ) ); + + Collection authorities = user.getAuthorities( ); + if ( authorities != null && !authorities.isEmpty( ) ) { + sb.append( " (" ); + boolean first = true; + for (GrantedAuthority a : authorities) { + if ( first ) { + sb.append( a.getAuthority( ) ); + first = false; + } else { + sb.append( ", " ).append( a.getAuthority( ) ); + } + } + sb.append( ")" ); + } + return sb.toString( ); + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c23500e..d72d137 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,8 +4,16 @@ logging.level.org.springframework=WARN logging.level.web=DEBUG #logging.pattern.console=%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx} logging.pattern.console=%d{HH:mm:ss.SSS} [${LOG_LEVEL_PATTERN}] %c{1}:%L - %m%n -pathToPublish=D:\\java +pathToPublish=C:\\docs explorer.template.path=classpath:/templates/ explorer.template.cacheEnabled=true +server.port=3630 spring.servlet.multipart.max-file-size=50MB spring.servlet.multipart.max-request-size=50MB +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..23ef134 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,90 @@ +DROP TABLE IF EXISTS APP_USER; +DROP TABLE IF EXISTS APP_ROLE; +DROP TABLE IF EXISTS Persistent_Logins; +DROP TABLE IF EXISTS USER_ROLE; +create table APP_USER +( + USER_ID BIGINT not null, + USER_NAME VARCHAR(36) not null, + ENCRYTED_PASSWORD VARCHAR(128) not null, + ENABLED BIT not null +) ; +-- +alter table APP_USER + add constraint APP_USER_PK primary key (USER_ID); + +alter table APP_USER + add constraint APP_USER_UK unique (USER_NAME); + + +-- Create table +create table APP_ROLE +( + ROLE_ID BIGINT not null, + ROLE_NAME VARCHAR(30) not null +) ; +-- +alter table APP_ROLE + add constraint APP_ROLE_PK primary key (ROLE_ID); + +alter table APP_ROLE + add constraint APP_ROLE_UK unique (ROLE_NAME); + + +-- Create table +create table USER_ROLE +( + ID BIGINT not null, + USER_ID BIGINT not null, + ROLE_ID BIGINT not null +); +-- +alter table USER_ROLE + add constraint USER_ROLE_PK primary key (ID); + +alter table USER_ROLE + add constraint USER_ROLE_UK unique (USER_ID, ROLE_ID); + +alter table USER_ROLE + add constraint USER_ROLE_FK1 foreign key (USER_ID) + references APP_USER (USER_ID); + +alter table USER_ROLE + add constraint USER_ROLE_FK2 foreign key (ROLE_ID) + references APP_ROLE (ROLE_ID); + + +-- Used by Spring Remember Me API. +CREATE TABLE Persistent_Logins ( + + username varchar(64) not null, + series varchar(64) not null, + token varchar(64) not null, + last_used timestamp not null, + PRIMARY KEY (series) + +); +insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED) +values (2, 'user', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1); + +insert into App_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED) +values (1, 'admin', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1); + +--- + +insert into app_role (ROLE_ID, ROLE_NAME) +values (1, 'ROLE_ADMIN'); + +insert into app_role (ROLE_ID, ROLE_NAME) +values (2, 'ROLE_USER'); + +--- + +insert into user_role (ID, USER_ID, ROLE_ID) +values (1, 1, 1); + +insert into user_role (ID, USER_ID, ROLE_ID) +values (2, 1, 2); + +insert into user_role (ID, USER_ID, ROLE_ID) +values (3, 2, 2); \ No newline at end of file diff --git a/src/main/resources/templates/base.ftl b/src/main/resources/templates/base.ftl index a2eb3c5..1adf251 100644 --- a/src/main/resources/templates/base.ftl +++ b/src/main/resources/templates/base.ftl @@ -2,24 +2,25 @@ <#setting url_escaping_charset="UTF-8"> <#macro common_page_head> - - - - - + + + + + <#macro page_head> - <@common_page_head /> + <@common_page_head /> <#macro render_whole_page> - - - - <@page_head /> - - - <@page_body /> - - + + + + <@page_head /> + + + <@page_body /> + + + diff --git a/src/main/resources/templates/error-page.ftl b/src/main/resources/templates/error-page.ftl index bd853b1..44c919b 100644 --- a/src/main/resources/templates/error-page.ftl +++ b/src/main/resources/templates/error-page.ftl @@ -5,8 +5,8 @@ <#macro page_body> -
-
${errorMessage}
-
+
+
${errorMessage}
+
<@render_whole_page /> diff --git a/src/main/resources/templates/filesView.ftl b/src/main/resources/templates/filesView.ftl index 8118821..fb21889 100644 --- a/src/main/resources/templates/filesView.ftl +++ b/src/main/resources/templates/filesView.ftl @@ -10,22 +10,22 @@ <#assign size = "—"/> <#assign picture_source = 'folder.svg'/> -
+
<#if file_elem.directory> - - - ${file_elem.name} - + + + ${file_elem.name} + <#else> -
- - ${file_elem.name} -
+
+ + ${file_elem.name} +
${file_elem.lastModifiedTime?date}
diff --git a/src/main/resources/templates/index.ftl b/src/main/resources/templates/index.ftl index 23b3649..15b56a0 100644 --- a/src/main/resources/templates/index.ftl +++ b/src/main/resources/templates/index.ftl @@ -5,81 +5,121 @@ File Manager <#macro page_body> - -
- -

- -

-
-
+ +
+
+
+ Name +
+
+
Date of change
+
Size
+
+
+
+ <#import "filesView.ftl" as macro> + <@macro.show_all directory/> +
-
-
- Name -
-
-
Date of change
-
Size
-
-
-
- <#import "filesView.ftl" as macro> - <@macro.show_all directory/> -
-
+ <@render_whole_page /> +
+ <#if RequestParameters.path??> + <#assign path = RequestParameters.path/> + <#else > + <#assign path=""/> + +
+
+ Create directory + +
+ + + +
+ + + diff --git a/src/main/resources/templates/loginPage.ftl b/src/main/resources/templates/loginPage.ftl new file mode 100644 index 0000000..2508e8b --- /dev/null +++ b/src/main/resources/templates/loginPage.ftl @@ -0,0 +1,45 @@ + + + + Login + + + + + + + + +

Login

+ +
+ + + + + + + + + + + + + + + +
User:
Password:
+ +
+
+ +
+Username/pass: +
    +
  • user 123
  • +
  • admin 123
  • +
+ + + + \ No newline at end of file