From ba39cbb1f371746eb182801d460b020f04495f93 Mon Sep 17 00:00:00 2001 From: umjammer <umjammer@gmail.com> Date: Sat, 9 May 2020 01:22:47 +0900 Subject: [PATCH] make it thread safe --- pom.xml | 2 +- .../core/filters/WebdavForBrowserFilter.java | 13 ++ src/main/java/vavi/apps/webdav/Config.java | 101 ++++++-- src/main/java/vavi/apps/webdav/Main.java | 8 +- .../java/vavi/apps/webdav/SecurityConfig.java | 11 + .../java/vavi/net/webdav/MyBeanFactory.java | 65 ++++++ .../java/vavi/net/webdav/SqlStrageDao.java | 27 ++- .../java/vavi/net/webdav/WebdavService.java | 221 +++++------------- .../webdav/auth/BasicWebTokenRefresher.java | 10 +- .../net/webdav/auth/box/BoxWebOAuth2.java | 8 +- .../webdav/auth/dropbox/DropBoxWebOAuth2.java | 11 +- src/main/resources/templates/index.html | 3 + src/main/resources/templates/onedrive.html | 4 +- src/test/java/Test2.java | 30 ++- 14 files changed, 300 insertions(+), 214 deletions(-) create mode 100644 src/main/java/vavi/net/webdav/MyBeanFactory.java diff --git a/pom.xml b/pom.xml index 71fef1f..4b72589 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ $ git push heroku master && heroku logs -t <dependency> <groupId>com.github.umjammer</groupId> <artifactId>vavi-apps-fuse</artifactId> - <version>0.0.8</version> + <version>0.0.9</version> <exclusions> <exclusion> <groupId>org.apache.jackrabbit</groupId> diff --git a/src/main/java/org/cryptomator/webdav/core/filters/WebdavForBrowserFilter.java b/src/main/java/org/cryptomator/webdav/core/filters/WebdavForBrowserFilter.java index f4d4c88..c1f5c51 100644 --- a/src/main/java/org/cryptomator/webdav/core/filters/WebdavForBrowserFilter.java +++ b/src/main/java/org/cryptomator/webdav/core/filters/WebdavForBrowserFilter.java @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2020 by Naohide Sano, All rights reserved. + * + * Programmed by Naohide Sano + */ + package org.cryptomator.webdav.core.filters; import java.io.IOException; @@ -11,6 +17,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +/** + * WebdavForBrowserFilter. + * + * @author <a href="mailto:umjammer@gmail.com">Naohide Sano</a> (umjammer) + * @version 0.00 2020/05/08 umjammer initial version <br> + */ public class WebdavForBrowserFilter implements HttpFilter { private static final Logger LOG = LoggerFactory.getLogger(WebdavForBrowserFilter.class); diff --git a/src/main/java/vavi/apps/webdav/Config.java b/src/main/java/vavi/apps/webdav/Config.java index 258bd59..7acfae9 100644 --- a/src/main/java/vavi/apps/webdav/Config.java +++ b/src/main/java/vavi/apps/webdav/Config.java @@ -6,7 +6,13 @@ package vavi.apps.webdav; +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; import javax.sql.DataSource; @@ -15,13 +21,18 @@ import org.cryptomator.webdav.core.filters.UnicodeResourcePathNormalizationFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall; +import com.github.fge.filesystem.box.provider.BoxFileSystemProvider; +import com.github.fge.fs.dropbox.provider.DropBoxFileSystemProvider; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -29,6 +40,8 @@ import vavi.net.webdav.SqlStrageDao; import vavi.net.webdav.WebdavService; import vavi.net.webdav.auth.StrageDao; +import vavi.net.webdav.auth.WebOAuth2; +import vavi.net.webdav.auth.WebUserCredential; import vavi.net.webdav.auth.box.BoxWebAppCredential; import vavi.net.webdav.auth.box.BoxWebOAuth2; import vavi.net.webdav.auth.dropbox.DropBoxWebAppCredential; @@ -37,6 +50,8 @@ import vavi.net.webdav.auth.google.GoogleWebAuthenticator; import vavi.net.webdav.auth.microsoft.MicrosoftWebAppCredential; import vavi.net.webdav.auth.microsoft.MicrosoftWebOAuth2; +import vavi.nio.file.googledrive.GoogleDriveFileSystemProvider; +import vavi.nio.file.onedrive4.OneDriveFileSystemProvider; /** @@ -46,8 +61,11 @@ * @version 0.00 2020/05/05 umjammer initial version <br> */ @Configuration +@ComponentScan("vavi.net.webdav") public class Config { +// private static final Logger LOG = LoggerFactory.getLogger(Config.class); + @Value("${spring.datasource.url}") private String dbUrl; @@ -129,28 +147,79 @@ public DropBoxWebAppCredential dropboxAppCredential() { } @Bean - public MicrosoftWebOAuth2 microsoftWebOAuth2(@Autowired MicrosoftWebAppCredential microsoftAppCredential) { - return new MicrosoftWebOAuth2(microsoftAppCredential); - } - - @Bean - public GoogleWebAuthenticator googleWebOAuth2(@Autowired GoogleWebAppCredential googleAppCredential) { - return new GoogleWebAuthenticator(googleAppCredential); + public WebdavService webdavService() { + return new WebdavService(); } - @Bean - public BoxWebOAuth2 boxWebOAuth2(@Autowired BoxWebAppCredential boxAppCredential) { - return new BoxWebOAuth2(boxAppCredential); + @Autowired + MicrosoftWebAppCredential microsoftAppCredential; + @Autowired + GoogleWebAppCredential googleAppCredential; + @Autowired + BoxWebAppCredential boxAppCredential; + @Autowired + DropBoxWebAppCredential dropboxAppCredential; + + @Bean + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public WebOAuth2<?, ?> getWebOAuth2(String scheme) throws IOException { + switch (scheme) { + case "onedrive": + return new MicrosoftWebOAuth2(microsoftAppCredential); + case "googledrive": + return new GoogleWebAuthenticator(googleAppCredential); + case "box": + return new BoxWebOAuth2(boxAppCredential); + case "dropbox": + return new DropBoxWebOAuth2(dropboxAppCredential); + default: + throw new IllegalArgumentException("unsupported scheme: " + scheme); + } } + /** + * @param id 'scheme:id' i.e. 'onedrive:foo@bar.com' + */ @Bean - public DropBoxWebOAuth2 dropBoxWebOAuth2(@Autowired DropBoxWebAppCredential dropboxAppCredential) { - return new DropBoxWebOAuth2(dropboxAppCredential); - } + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public FileSystem getFileSystem(String id) throws IOException { + String[] part1s = id.split(":"); + if (part1s.length < 2) { + throw new IllegalArgumentException("bad 2nd path component: should be 'scheme:id' i.e. 'onedrive:foo@bar.com'"); + } + String scheme = part1s[0]; + String idForScheme = part1s[1]; + + URI uri = URI.create(scheme + ":///"); + Map<String, Object> env = new HashMap<>(); + switch (scheme) { + case "onedrive": + env.put(OneDriveFileSystemProvider.ENV_APP_CREDENTIAL, microsoftAppCredential); + env.put(OneDriveFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); + env.put("ignoreAppleDouble", true); + break; + case "googledrive": + env.put(GoogleDriveFileSystemProvider.ENV_APP_CREDENTIAL, googleAppCredential); + env.put(GoogleDriveFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); + env.put("ignoreAppleDouble", true); + break; + case "box": + env.put(BoxFileSystemProvider.ENV_APP_CREDENTIAL, boxAppCredential); + env.put(BoxFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); + env.put("ignoreAppleDouble", true); + break; + case "dropbox": + env.put(DropBoxFileSystemProvider.ENV_APP_CREDENTIAL, dropboxAppCredential); + env.put(DropBoxFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); + env.put("ignoreAppleDouble", true); + break; + default: + throw new IllegalArgumentException("unsupported scheme: " + scheme); + } - @Bean - public WebdavService webdavService() { - return new WebdavService(); + // https://github.com/spring-projects/spring-boot/issues/7110#issuecomment-252247036 + FileSystem fs = FileSystems.newFileSystem(uri, env, Thread.currentThread().getContextClassLoader()); + return fs; } } diff --git a/src/main/java/vavi/apps/webdav/Main.java b/src/main/java/vavi/apps/webdav/Main.java index 1ec21a0..eb21056 100644 --- a/src/main/java/vavi/apps/webdav/Main.java +++ b/src/main/java/vavi/apps/webdav/Main.java @@ -87,7 +87,7 @@ String microsoft(Model model, } else { model.addAttribute("error", error); model.addAttribute("errorDescription", errorDescription); - return "onedrive"; + return "app_error"; } } @@ -103,7 +103,7 @@ String box(Model model, } else { model.addAttribute("error", error); model.addAttribute("errorDescription", errorDescription); - return "onedrive"; + return "app_error"; } } @@ -119,7 +119,7 @@ String dropbox(Model model, } else { model.addAttribute("error", error); model.addAttribute("errorDescription", errorDescription); - return "onedrive"; + return "app_error"; } } @@ -135,7 +135,7 @@ String google(Model model, } else { model.addAttribute("error", error); model.addAttribute("errorDescription", errorDescription); - return "onedrive"; + return "app_error"; } } } diff --git a/src/main/java/vavi/apps/webdav/SecurityConfig.java b/src/main/java/vavi/apps/webdav/SecurityConfig.java index 7571b5e..10a5305 100644 --- a/src/main/java/vavi/apps/webdav/SecurityConfig.java +++ b/src/main/java/vavi/apps/webdav/SecurityConfig.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2019 by Naohide Sano, All rights reserved. + * + * Programmed by Naohide Sano + */ package vavi.apps.webdav; @@ -10,6 +15,12 @@ import vavi.net.webdav.JavaFsWebDavServlet; +/** + * SecurityConfig. + * + * @author <a href="mailto:umjammer@gmail.com">Naohide Sano</a> (umjammer) + * @version 0.00 2020/05/08 umjammer initial version <br> + */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { diff --git a/src/main/java/vavi/net/webdav/MyBeanFactory.java b/src/main/java/vavi/net/webdav/MyBeanFactory.java new file mode 100644 index 0000000..5883dc8 --- /dev/null +++ b/src/main/java/vavi/net/webdav/MyBeanFactory.java @@ -0,0 +1,65 @@ +/* + * https://stackoverflow.com/questions/12537851/accessing-spring-beans-in-static-method + */ + +package vavi.net.webdav; + +import java.io.IOException; +import java.nio.file.FileSystem; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import vavi.net.webdav.auth.OAuthException; +import vavi.net.webdav.auth.WebOAuth2; + + +/** + * gather spring dependencies. + * + * @author <a href="mailto:umjammer@gmail.com">Naohide Sano</a> (umjammer) + * @version 0.00 2020/05/08 umjammer initial version <br> + */ +@Service +public class MyBeanFactory implements InitializingBean { + private static MyBeanFactory instance; + + @Autowired + BeanFactory beanFactory; + + private FileSystem getFileSystemInternal(String id) throws IOException { + try { + FileSystem fs = beanFactory.getBean(FileSystem.class, id); + return fs; + } catch (BeanCreationException e) { + OAuthException t = findRootCause(e, OAuthException.class); + throw t != null ? t : e; + } + } + + private <T extends Throwable> T findRootCause(Throwable t, Class<T> clazz) { + while (t.getCause() != null) { + t = t.getCause(); + if (clazz.isInstance(t)) { + return clazz.cast(t); + } + } + return null; + } + + @Override + public void afterPropertiesSet() throws Exception { + instance = this; + } + + public static FileSystem getFileSystem(String id) throws IOException { + return instance.getFileSystemInternal(id); + } + + public static WebOAuth2<?, ?> getWebOAuth2(String scheme) { + return instance.beanFactory.getBean(WebOAuth2.class, scheme); + } +} \ No newline at end of file diff --git a/src/main/java/vavi/net/webdav/SqlStrageDao.java b/src/main/java/vavi/net/webdav/SqlStrageDao.java index e632491..cc516bf 100644 --- a/src/main/java/vavi/net/webdav/SqlStrageDao.java +++ b/src/main/java/vavi/net/webdav/SqlStrageDao.java @@ -17,6 +17,8 @@ import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import vavi.net.webdav.auth.StrageDao; @@ -30,7 +32,7 @@ */ public class SqlStrageDao implements StrageDao { -// private static final Logger LOG = LoggerFactory.getLogger(PostgressStrageDao.class); + private static final Logger LOG = LoggerFactory.getLogger(SqlStrageDao.class); @Autowired private DataSource dataSource; @@ -53,8 +55,10 @@ public List<String> load() { @Override public void update(String id, String token) { try (Connection connection = dataSource.getConnection()) { - Statement stmt = connection.createStatement(); - stmt.executeUpdate("UPDATE credential SET token = '" + token + "' WHERE id = '" + id + "'"); + PreparedStatement pstmt = connection.prepareStatement("UPDATE credential SET token = ? WHERE id = ?"); + pstmt.setString(1, token); + pstmt.setString(2, id); + pstmt.execute(); } catch (SQLException e) { throw new IllegalStateException(e); } @@ -63,9 +67,11 @@ public void update(String id, String token) { @Override public String select(String id) { try (Connection connection = dataSource.getConnection()) { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT token FROM credential WHERE id = '" + id + "'"); + PreparedStatement pstmt = connection.prepareStatement("SELECT token FROM credential WHERE id = ?"); + pstmt.setString(1, id); + ResultSet rs = pstmt.executeQuery(); if (rs.next()) { +LOG.debug("[" + id + "]: " + rs.getString("token")); return rs.getString("token"); } else { return null; @@ -75,11 +81,14 @@ public String select(String id) { } } + private static final String googleId = "StoredCredential"; + @Override public byte[] selectGoogle() { try (Connection connection = dataSource.getConnection()) { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT * FROM google WHERE id = 'StoredCredential'"); + PreparedStatement pstmt = connection.prepareStatement("SELECT * FROM google WHERE id = ?"); + pstmt.setString(1, googleId); + ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return rs.getBytes("credentials"); @@ -94,9 +103,9 @@ public byte[] selectGoogle() { public void updateGoogle(byte[] credentials) { try (Connection connection = dataSource.getConnection()) { connection.setAutoCommit(false); - PreparedStatement pstmt = connection - .prepareStatement("UPDATE google SET credentials = ? WHERE id = 'StoredCredential'"); + PreparedStatement pstmt = connection.prepareStatement("UPDATE google SET credentials = ? WHERE id = ?"); pstmt.setBytes(1, credentials); + pstmt.setString(2, googleId); pstmt.execute(); connection.commit(); } catch (SQLException e) { diff --git a/src/main/java/vavi/net/webdav/WebdavService.java b/src/main/java/vavi/net/webdav/WebdavService.java index a32f825..28a6197 100644 --- a/src/main/java/vavi/net/webdav/WebdavService.java +++ b/src/main/java/vavi/net/webdav/WebdavService.java @@ -8,8 +8,6 @@ import java.io.IOException; import java.net.URI; -import java.net.URLDecoder; -import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -22,28 +20,12 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import com.github.fge.filesystem.box.provider.BoxFileSystemProvider; -import com.github.fge.fs.dropbox.provider.DropBoxFileSystemProvider; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; - -import vavi.net.auth.oauth2.AppCredential; import vavi.net.http.HttpUtil; import vavi.net.webdav.auth.OAuthException; import vavi.net.webdav.auth.StrageDao; import vavi.net.webdav.auth.WebOAuth2; -import vavi.net.webdav.auth.WebUserCredential; -import vavi.net.webdav.auth.box.BoxWebAppCredential; -import vavi.net.webdav.auth.box.BoxWebOAuth2; -import vavi.net.webdav.auth.dropbox.DropBoxWebAppCredential; -import vavi.net.webdav.auth.dropbox.DropBoxWebOAuth2; -import vavi.net.webdav.auth.google.GoogleWebAppCredential; -import vavi.net.webdav.auth.google.GoogleWebAuthenticator; -import vavi.net.webdav.auth.microsoft.MicrosoftWebAppCredential; -import vavi.net.webdav.auth.microsoft.MicrosoftWebOAuth2; import vavi.nio.file.gathered.GatheredFileSystemProvider; -import vavi.nio.file.googledrive.GoogleDriveFileSystemProvider; -import vavi.nio.file.onedrive4.OneDriveFileSystemProvider; +import vavi.nio.file.gathered.NameMap; /** @@ -56,89 +38,67 @@ public class WebdavService { private static final Logger LOG = LoggerFactory.getLogger(WebdavService.class); - /** */ - public static final String URL_ROOT_PATH = "webdav"; - - /** <scheme, Path> */ - private Map<String, Path> rootPaths = new HashMap<>(); - - /** */ - private Path gatheredFsRoot; + @Autowired + private StrageDao dao; /** <id, FileSystem> */ private Map<String, FileSystem> fileSystems = new HashMap<>(); - /** <scheme, BasicAppCredential> */ - private Map<String, AppCredential> appCredentials = new HashMap<>(); + /** <id, String> */ + private NameMap nameMap = new NameMap(); - /** <scheme, WebOAuth2> */ - private Map<String, WebOAuth2<?, ?>> oauth2s = new HashMap<>(); + /** */ + private FileSystem registerFileSystem(String id) throws IOException { + FileSystem fs = MyBeanFactory.getFileSystem(id); + fileSystems.put(id, fs); + nameMap.put(id, id.replaceAll("[:@\\.]", "_")); + return fs; + } - @Autowired - MicrosoftWebAppCredential microsoftAppCredential; - @Autowired - GoogleWebAppCredential googleAppCredential; - @Autowired - BoxWebAppCredential boxAppCredential; - @Autowired - DropBoxWebAppCredential dropboxAppCredential; + /** */ + private void deregisterFileSystem(String id) { + fileSystems.remove(id); + nameMap.remove(id); + } - @Autowired - MicrosoftWebOAuth2 microsoftWebOAuth2; - @Autowired - GoogleWebAuthenticator googleWebOAuth2; - @Autowired - BoxWebOAuth2 boxWebOAuth2; - @Autowired - DropBoxWebOAuth2 dropBoxWebOAuth2; - - private boolean inited = false; - - /** @see #inited */ - private void preInit() { - if (!inited) { - appCredentials.put("onedrive", microsoftAppCredential); - appCredentials.put("box", boxAppCredential); - appCredentials.put("dropbox", dropboxAppCredential); - appCredentials.put("googledrive", googleAppCredential); - -appCredentials.forEach((k, v) -> System.err.println(k + ": " + v)); - - oauth2s.put("onedrive", microsoftWebOAuth2); - oauth2s.put("googledrive", googleWebOAuth2); - oauth2s.put("box", boxWebOAuth2); - oauth2s.put("dropbox", dropBoxWebOAuth2); - -oauth2s.forEach((k, v) -> System.err.println(k + ": " + v)); - - Map<String, String> nameMap = new HashMap<>(); - this.nameMap = HashBiMap.create(nameMap); - - dao.load().forEach(id -> { - try { -LOG.debug("add: " + id); - FileSystem fs = getFileSystem(id); - fileSystems.put(id, fs); - } catch (OAuthException e) { -LOG.info("locked: " + id + ": " + e.getMessage()); - } catch (Exception e) { -LOG.warn(id, e); + /** + * should call after di + */ + private Map<String, OAuthException> prepareFileSystems() { + + Map<String, OAuthException> errors= new HashMap<>(); + + dao.load().forEach(id -> { + try { +LOG.debug("ADD: " + id + " ---------------------------------------------------"); + if (!fileSystems.containsKey(id)) { + registerFileSystem(id); } - }); + } catch (OAuthException e) { +LOG.info("NOT LOGINED: " + id + ": " + e.getMessage()); + errors.put(id, e); + deregisterFileSystem(id); + } catch (Exception e) { +LOG.warn(id, e); + deregisterFileSystem(id); + } + }); - inited = true; - } + return errors; } - /** <id, String> */ - private BiMap<String, String> nameMap; + /** */ + public static final String URL_ROOT_PATH = "webdav"; - @Autowired - private StrageDao dao; + /** <scheme, Path> */ + private Map<String, Path> rootPaths = new HashMap<>(); + + /** */ + private Path gatheredFsRoot; /** */ public void init() throws IOException { - preInit(); + prepareFileSystems(); URI uri = URI.create("gatheredfs:///"); Map<String, Object> env = new HashMap<>(); @@ -149,18 +109,6 @@ public void init() throws IOException { gatheredFsRoot = fs.getRootDirectories().iterator().next(); } - /** - * @param path apparent name - * @return real id - */ - private String decode(String path) throws IOException { - if (nameMap.size() > 0) { - return nameMap.inverse().get(path); - } else { - return URLDecoder.decode(path, Charset.forName("utf-8").name()); - } - } - /** */ public Path resolve(String relativeUrl) throws IOException { String[] parts = relativeUrl.split("/"); @@ -172,7 +120,7 @@ public Path resolve(String relativeUrl) throws IOException { return gatheredFsRoot; } - String id = decode(parts[1]); + String id = nameMap.decodeFsName(parts[1]); System.err.println(id); String path = String.join("/", Arrays.copyOfRange(parts, 2, parts.length)); @@ -184,7 +132,8 @@ public Path resolve(String relativeUrl) throws IOException { if (fileSystems.containsKey(id)) { fs = fileSystems.get(id); } else { - fs = getFileSystem(id); + fs = registerFileSystem(id); + // TODO assert false "???"; } rootPath = fs.getRootDirectories().iterator().next(); if (!Files.exists(rootPath)) { @@ -197,73 +146,11 @@ public Path resolve(String relativeUrl) throws IOException { return rootPath.resolve(path); } - /** - * @param id "scheme:your@id.com" - */ - private FileSystem getFileSystem(String id) throws IOException { - String[] part1s = id.split(":"); - if (part1s.length < 2) { - throw new IllegalArgumentException("bad 2nd path component: should be 'scheme:id' i.e. 'onedrive:foo@bar.com'"); - } - String scheme = part1s[0]; - String idForScheme = part1s[1]; - - URI uri = URI.create(scheme + ":///"); - Map<String, Object> env = new HashMap<>(); - switch (scheme) { - case "onedrive": - env.put(OneDriveFileSystemProvider.ENV_APP_CREDENTIAL, appCredentials.get(scheme)); - env.put(OneDriveFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); - env.put("ignoreAppleDouble", true); - break; - case "googledrive": - env.put(GoogleDriveFileSystemProvider.ENV_APP_CREDENTIAL, appCredentials.get(scheme)); - env.put(GoogleDriveFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); - env.put("ignoreAppleDouble", true); - break; - case "box": - env.put(BoxFileSystemProvider.ENV_APP_CREDENTIAL, appCredentials.get(scheme)); - env.put(BoxFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); - env.put("ignoreAppleDouble", true); - break; - case "dropbox": - env.put(DropBoxFileSystemProvider.ENV_APP_CREDENTIAL, appCredentials.get(scheme)); - env.put(DropBoxFileSystemProvider.ENV_USER_CREDENTIAL, new WebUserCredential(idForScheme)); - env.put("ignoreAppleDouble", true); - break; - case "vfs": - default: - throw new IllegalArgumentException("unsupported scheme: " + scheme); - } - - nameMap.put(id, id.replaceAll("[:@\\.]", "_")); - - // https://github.com/spring-projects/spring-boot/issues/7110#issuecomment-252247036 - FileSystem fs = FileSystems.newFileSystem(uri, env, Thread.currentThread().getContextClassLoader()); - return fs; - } - /** for view:/admin/list */ public Map<?, ?>[] getStrageStatus() { - preInit(); - - Map<String, OAuthException> errors= new HashMap<>(); - - dao.load().forEach(id -> { - try { -LOG.debug("LIST: " + id + " ---------------------------------------------------"); - if (!fileSystems.containsKey(id)) { - fileSystems.put(id, getFileSystem(id)); - } - } catch (OAuthException e) { -LOG.debug("NOT LOGINED: " + id); - errors.put(id, e); - } catch (Exception e) { - LOG.warn(id, e); - } - }); + Map<String, OAuthException> errors = prepareFileSystems(); - return new Map[] { fileSystems, errors, nameMap }; + return new Map[] { fileSystems, errors, nameMap.map() }; } /** <state, WebOAuth2> */ @@ -280,7 +167,7 @@ public String login(String id) { String[] parts = id.split(":"); String scheme = parts[0]; String idForScheme = parts[1]; - WebOAuth2<?, ?> oauth2 = oauth2s.get(scheme); + WebOAuth2<?, ?> oauth2 = MyBeanFactory.getWebOAuth2(scheme); URI uri = oauth2.getAuthorizationUrl(); LOG.debug("authorizationUrl: " + uri); String state = HttpUtil.splitQuery(uri).get("state")[0]; @@ -302,6 +189,8 @@ public void auth(String code, String state) { String id = sessionIds.get(state); oauth2.setResult(code, state); oauth2.authorize(id); + sessionOAuth2s.remove(state); + sessionIds.remove(state); LOG.debug("auth done: " + id); } catch (IOException e) { throw new IllegalStateException(e); diff --git a/src/main/java/vavi/net/webdav/auth/BasicWebTokenRefresher.java b/src/main/java/vavi/net/webdav/auth/BasicWebTokenRefresher.java index b7d33f8..d7b7841 100644 --- a/src/main/java/vavi/net/webdav/auth/BasicWebTokenRefresher.java +++ b/src/main/java/vavi/net/webdav/auth/BasicWebTokenRefresher.java @@ -9,9 +9,11 @@ import java.io.IOException; import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import vavi.net.auth.oauth2.AppCredential; import vavi.net.auth.oauth2.BaseTokenRefresher; -import vavi.util.Debug; /** @@ -22,6 +24,8 @@ */ public class BasicWebTokenRefresher extends BaseTokenRefresher<String> { + private static final Logger LOG = LoggerFactory.getLogger(BasicWebTokenRefresher.class); + private String schemeId; private WebAppCredential appCredential; @@ -35,14 +39,14 @@ public BasicWebTokenRefresher(AppCredential appCredential, String id, Supplier<L @Override public void writeRefreshToken(String refreshToken) throws IOException { -Debug.println("save refreshToken: " + refreshToken); +LOG.debug("save refreshToken [" + schemeId + "]: " + refreshToken); appCredential.getStrageDao().update(schemeId, refreshToken); } @Override public String readRefreshToken() throws IOException { String refreshToken = appCredential.getStrageDao().select(schemeId); -Debug.println("load refreshToken: " + refreshToken); +LOG.debug("load refreshToken [" + schemeId + "]: " + refreshToken); return refreshToken; } diff --git a/src/main/java/vavi/net/webdav/auth/box/BoxWebOAuth2.java b/src/main/java/vavi/net/webdav/auth/box/BoxWebOAuth2.java index 7ca3976..878fe43 100644 --- a/src/main/java/vavi/net/webdav/auth/box/BoxWebOAuth2.java +++ b/src/main/java/vavi/net/webdav/auth/box/BoxWebOAuth2.java @@ -10,11 +10,13 @@ import java.net.URI; import java.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.box.sdk.BoxAPIConnection; import vavi.net.webdav.auth.WebAppCredential; import vavi.net.webdav.auth.WebOAuth2; -import vavi.util.Debug; import vavi.util.StringUtil; @@ -28,6 +30,8 @@ */ public class BoxWebOAuth2 implements WebOAuth2<String, BoxAPIConnection> { + private static final Logger LOG = LoggerFactory.getLogger(BoxWebOAuth2.class); + /** */ private WebAppCredential appCredential; @@ -47,7 +51,7 @@ public BoxAPIConnection authorize(String id) throws IOException { api.setAutoRefresh(true); String save = api.save(); -Debug.println("save: " + save); +LOG.debug("save [" + appCredential.getScheme() + ":" + id + "]: " + save); appCredential.getStrageDao().update(appCredential.getScheme() + ":" + id, save); return api; diff --git a/src/main/java/vavi/net/webdav/auth/dropbox/DropBoxWebOAuth2.java b/src/main/java/vavi/net/webdav/auth/dropbox/DropBoxWebOAuth2.java index b9d734b..4b1c76a 100644 --- a/src/main/java/vavi/net/webdav/auth/dropbox/DropBoxWebOAuth2.java +++ b/src/main/java/vavi/net/webdav/auth/dropbox/DropBoxWebOAuth2.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.util.Locale; import javax.servlet.http.HttpSession; @@ -53,7 +52,7 @@ public class DropBoxWebOAuth2 implements WebOAuth2<String, String> { private DbxWebAuth webAuth; /** */ - private String authorizeUrl; + private URI authorizeUrl; /** */ private String redirectUri; @@ -77,7 +76,7 @@ public DropBoxWebOAuth2(WebAppCredential appCredential) { webAuth = new DbxWebAuth(requestConfig, appInfo); Request request = Request.newBuilder().withRedirectUri(appCredential.getRedirectUrl(), csrfTokenStore).build(); - authorizeUrl = webAuth.authorize(request); + authorizeUrl = URI.create(webAuth.authorize(request)); } /** */ @@ -97,11 +96,7 @@ public String authorize(String id) throws IOException { @Override public URI getAuthorizationUrl() { - try { - return new URI(authorizeUrl); - } catch (URISyntaxException e) { - throw new IllegalStateException(e); - } + return authorizeUrl; } @Override diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 78b65bf..b494b3c 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -57,4 +57,7 @@ <h3><span class="glyphicon glyphicon-link"></span> Helpful Links</h3> </div> </body> +<footer> +<a href="https://icons8.com/icon/44790/cloud-storage">Cloud Storage icon by Icons8</a> +</footer> </html> diff --git a/src/main/resources/templates/onedrive.html b/src/main/resources/templates/onedrive.html index 6a22eb6..e89ef22 100644 --- a/src/main/resources/templates/onedrive.html +++ b/src/main/resources/templates/onedrive.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'index')}"> <head> - <title>Onedrive OAuth Redirection</title> + <title>OAuth2 Redirection</title> <link rel="stylesheet" type="text/css" href="/stylesheets/main.css" /> </head> @@ -9,7 +9,7 @@ <div class="container"> -<h1>Onedrive</h1> +<h1>OAuth2 Error</h1> <h2>[[${error}]]</h2> diff --git a/src/test/java/Test2.java b/src/test/java/Test2.java index a309948..8a600e7 100644 --- a/src/test/java/Test2.java +++ b/src/test/java/Test2.java @@ -10,6 +10,7 @@ import java.nio.file.Paths; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; @@ -40,12 +41,14 @@ public class Test2 { public static void main(String[] args) throws Exception { Test2 app = new Test2(); - PropsEntity.Util.bind(app); +// PropsEntity.Util.bind(app); System.err.println(app.url); // app.drop(); // app.create(); // app.insert(); +// app.update("box:your@id.com"); app.select(); +// app.select("box:your@id.com"); System.err.println("done"); } @@ -66,7 +69,7 @@ void create() throws SQLException, IOException { void insert() throws SQLException, IOException { try (Connection connection = DriverManager.getConnection(url, user, password)) { - Statement stmt = connection.createStatement(); + PreparedStatement pstmt = connection.prepareStatement("INSERT INTO credential VALUES (?, ?)"); LocalStrageDao local = new LocalStrageDao("tmp/database.properties"); for (String schemeId : local.load()) { @@ -84,13 +87,23 @@ void insert() throws SQLException, IOException { if (scheme.equals("msgraph")) { scheme = "onedrive"; } - stmt.executeUpdate("INSERT INTO credential VALUES ('" + scheme + ":" + email + "', '" + token + "')"); + pstmt.setString(1, scheme + ":" + email); + pstmt.setString(2, token); + pstmt.execute(); System.err.println("DB: id: " + schemeId); } } } } + void update(String id) throws SQLException, IOException { + try (Connection connection = DriverManager.getConnection(url, user, password)) { + PreparedStatement pstmt = connection.prepareStatement("UPDATE credential SET token = NULL WHERE id = ?"); + pstmt.setString(1, id); + pstmt.execute(); + } + } + void select() throws SQLException { try (Connection connection = DriverManager.getConnection(url, user, password)) { Statement stmt = connection.createStatement(); @@ -101,6 +114,17 @@ void select() throws SQLException { } } } + + void select(String id) throws SQLException { + try (Connection connection = DriverManager.getConnection(url, user, password)) { + PreparedStatement pstmt = connection.prepareStatement("SELECT token FROM credential WHERE id = ?"); + pstmt.setString(1, id); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { +System.err.println("DB: { id: " + id + ", token: " + rs.getString("token") + " }"); + } + } + } } /* */