diff --git a/src/main/java/org/codelibs/fess/app/web/base/FessBaseAction.java b/src/main/java/org/codelibs/fess/app/web/base/FessBaseAction.java index 51428cdbbcec5d7f8a01a19aeb0855c55f871889..e1eac2ffbb7f8eb4ba24c46f7c2a44bff7cc81ef 100644 --- a/src/main/java/org/codelibs/fess/app/web/base/FessBaseAction.java +++ b/src/main/java/org/codelibs/fess/app/web/base/FessBaseAction.java @@ -46,6 +46,8 @@ import org.lastaflute.web.servlet.session.SessionManager; import org.lastaflute.web.validation.ActionValidator; import org.lastaflute.web.validation.LaValidatable; import org.lastaflute.web.validation.VaMessenger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author jflute @@ -56,6 +58,8 @@ public abstract class FessBaseAction extends TypicalAction // has several interf // =================================================================================== // Definition // ========== + private static final Logger logger = LoggerFactory.getLogger(FessBaseAction.class); + /** The application type for FESs, e.g. used by access context. */ protected static final String APP_TYPE = "FES"; // #change_it_first @@ -105,6 +109,12 @@ public abstract class FessBaseAction extends TypicalAction // has several interf // you should remove the 'final' if you need to override this @Override public ActionResponse godHandPrologue(final ActionRuntime runtime) { + fessLoginAssist.getSavedUserBean().ifPresent(u -> { + boolean result = u.getFessUser().refresh(); + if (logger.isDebugEnabled()) { + logger.debug("refresh user info: {}", result); + } + }); return viewHelper.getActionHook().godHandPrologue(runtime, r -> super.godHandPrologue(r)); } diff --git a/src/main/java/org/codelibs/fess/app/web/base/login/AzureAdCredential.java b/src/main/java/org/codelibs/fess/app/web/base/login/AzureAdCredential.java index 0ccce04906c863888fb2c37654fc43d8f6b34e35..5c37b8ce8e081ed74d26d9e06c3bfc894dcfeabf 100644 --- a/src/main/java/org/codelibs/fess/app/web/base/login/AzureAdCredential.java +++ b/src/main/java/org/codelibs/fess/app/web/base/login/AzureAdCredential.java @@ -24,6 +24,7 @@ import java.util.Set; import org.codelibs.core.lang.StringUtil; import org.codelibs.fess.entity.FessUser; import org.codelibs.fess.helper.SystemHelper; +import org.codelibs.fess.sso.aad.AzureAdAuthenticator; import org.codelibs.fess.util.ComponentUtil; import org.lastaflute.web.login.credential.LoginCredential; @@ -47,8 +48,8 @@ public class AzureAdCredential implements LoginCredential, FessCredential { return "{" + authResult.getUserInfo().getDisplayableId() + "}"; } - public User getUser() { - return new User(authResult.getUserInfo().getDisplayableId(), getDefaultGroupsAsArray(), getDefaultRolesAsArray()); + public AzureAdUser getUser() { + return new AzureAdUser(authResult, getDefaultGroupsAsArray(), getDefaultRolesAsArray()); } protected static String[] getDefaultGroupsAsArray() { @@ -69,26 +70,26 @@ public class AzureAdCredential implements LoginCredential, FessCredential { } } - public static class User implements FessUser { + public static class AzureAdUser implements FessUser { private static final long serialVersionUID = 1L; - protected final String name; - protected String[] groups; protected String[] roles; protected String[] permissions; - protected User(final String name, final String[] groups, final String[] roles) { - this.name = name; + protected AuthenticationResult authResult; + + public AzureAdUser(final AuthenticationResult authResult, final String[] groups, final String[] roles) { + this.authResult = authResult; this.groups = groups; this.roles = roles; } @Override public String getName() { - return name; + return authResult.getUserInfo().getDisplayableId(); } @Override @@ -106,7 +107,7 @@ public class AzureAdCredential implements LoginCredential, FessCredential { if (permissions == null) { final SystemHelper systemHelper = ComponentUtil.getSystemHelper(); final Set<String> permissionSet = new HashSet<>(); - permissionSet.add(systemHelper.getSearchRoleByUser(name)); + permissionSet.add(systemHelper.getSearchRoleByUser(getName())); stream(groups).of(stream -> stream.forEach(s -> permissionSet.add(systemHelper.getSearchRoleByGroup(s)))); stream(roles).of(stream -> stream.forEach(s -> permissionSet.add(systemHelper.getSearchRoleByRole(s)))); permissions = permissionSet.toArray(new String[permissionSet.size()]); @@ -115,9 +116,14 @@ public class AzureAdCredential implements LoginCredential, FessCredential { } @Override - public boolean isEditable() { + public boolean refresh() { + if (authResult.getExpiresAfter() < System.currentTimeMillis()) { + return false; + } + final AzureAdAuthenticator authenticator = ComponentUtil.getComponent(AzureAdAuthenticator.class); + final String refreshToken = authResult.getRefreshToken(); + authResult = authenticator.getAccessToken(refreshToken); return false; } - } } diff --git a/src/main/java/org/codelibs/fess/app/web/base/login/OpenIdConnectCredential.java b/src/main/java/org/codelibs/fess/app/web/base/login/OpenIdConnectCredential.java index 52c68f194009f54237e8256fad9afdd88ba75e8f..0ce9e8fa035be7ed061e2a2c4ec5d4f46679eba7 100644 --- a/src/main/java/org/codelibs/fess/app/web/base/login/OpenIdConnectCredential.java +++ b/src/main/java/org/codelibs/fess/app/web/base/login/OpenIdConnectCredential.java @@ -46,8 +46,8 @@ public class OpenIdConnectCredential implements LoginCredential, FessCredential return (String) attributes.get("email"); } - public User getUser() { - return new User(getUserId(), getDefaultGroupsAsArray(), getDefaultRolesAsArray()); + public OpenIdUser getUser() { + return new OpenIdUser(getUserId(), getDefaultGroupsAsArray(), getDefaultRolesAsArray()); } protected static String[] getDefaultGroupsAsArray() { @@ -68,7 +68,7 @@ public class OpenIdConnectCredential implements LoginCredential, FessCredential } } - public static class User implements FessUser { + public static class OpenIdUser implements FessUser { private static final long serialVersionUID = 1L; @@ -80,7 +80,7 @@ public class OpenIdConnectCredential implements LoginCredential, FessCredential protected String[] permissions; - protected User(final String name, final String[] groups, final String[] roles) { + protected OpenIdUser(final String name, final String[] groups, final String[] roles) { this.name = name; this.groups = groups; this.roles = roles; @@ -114,10 +114,5 @@ public class OpenIdConnectCredential implements LoginCredential, FessCredential return permissions; } - @Override - public boolean isEditable() { - return false; - } - } } diff --git a/src/main/java/org/codelibs/fess/entity/FessUser.java b/src/main/java/org/codelibs/fess/entity/FessUser.java index 632d33737fe27a60b4d081beabc8f08bea27285b..7c5182acc054de089fa990a990037a3780357cab 100644 --- a/src/main/java/org/codelibs/fess/entity/FessUser.java +++ b/src/main/java/org/codelibs/fess/entity/FessUser.java @@ -27,5 +27,11 @@ public interface FessUser extends Serializable { String[] getPermissions(); - boolean isEditable(); + default boolean isEditable() { + return false; + } + + default boolean refresh() { + return false; + } } diff --git a/src/main/java/org/codelibs/fess/sso/aad/AzureAdAuthenticator.java b/src/main/java/org/codelibs/fess/sso/aad/AzureAdAuthenticator.java index b20b14f64e4348850a299a670bf117a361df6c29..b96ee99ecd674e030f8ab445d1e1e192be4c53f4 100644 --- a/src/main/java/org/codelibs/fess/sso/aad/AzureAdAuthenticator.java +++ b/src/main/java/org/codelibs/fess/sso/aad/AzureAdAuthenticator.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.annotation.PostConstruct; @@ -84,6 +85,8 @@ public class AzureAdAuthenticator implements SsoAuthenticator { protected static final String CODE = "code"; + protected long acquisitionTimeout = 30 * 1000L; + @PostConstruct public void init() { ComponentUtil.getSsoManager().register(this); @@ -104,10 +107,8 @@ public class AzureAdAuthenticator implements SsoAuthenticator { return null; } - // TODO refresh - - return new ActionResponseCredential(() -> HtmlResponse.fromRedirectPathAsIs(getAuthUrl(request))); - }).orElse(null); + return new ActionResponseCredential(() -> HtmlResponse.fromRedirectPathAsIs(getAuthUrl(request))); + }).orElse(null); } protected String getAuthUrl(final HttpServletRequest request) { @@ -214,6 +215,31 @@ public class AzureAdAuthenticator implements SsoAuthenticator { } } + public AuthenticationResult getAccessToken(String refreshToken) { + final String authority = getAuthority() + getTenant() + "/"; + if (logger.isDebugEnabled()) { + logger.debug("refreshToken: {}, authority: {}", refreshToken, authority); + } + ExecutorService service = null; + try { + service = Executors.newFixedThreadPool(1); + AuthenticationContext context = new AuthenticationContext(authority, true, service); + Future<AuthenticationResult> future = + context.acquireTokenByRefreshToken(refreshToken, new ClientCredential(getClientId(), getClientSecret()), null, null); + final AuthenticationResult result = future.get(acquisitionTimeout, TimeUnit.MILLISECONDS); + if (result == null) { + throw new SsoLoginException("authentication result was null"); + } + return result; + } catch (Exception e) { + throw new SsoLoginException("Failed to get a token.", e); + } finally { + if (service != null) { + service.shutdown(); + } + } + } + protected AuthenticationResult getAccessToken(final AuthorizationCode authorizationCode, final String currentUri) { final String authority = getAuthority() + getTenant() + "/"; final String authCode = authorizationCode.getValue(); @@ -223,11 +249,11 @@ public class AzureAdAuthenticator implements SsoAuthenticator { final ClientCredential credential = new ClientCredential(getClientId(), getClientSecret()); ExecutorService service = null; try { - service = Executors.newFixedThreadPool(1);// TODO replace with something... + service = Executors.newFixedThreadPool(1); final AuthenticationContext context = new AuthenticationContext(authority, true, service); final Future<AuthenticationResult> future = context.acquireTokenByAuthorizationCode(authCode, new URI(currentUri), credential, null); - final AuthenticationResult result = future.get(); + final AuthenticationResult result = future.get(acquisitionTimeout, TimeUnit.MILLISECONDS); if (result == null) { throw new SsoLoginException("authentication result was null"); } @@ -338,4 +364,8 @@ public class AzureAdAuthenticator implements SsoAuthenticator { return OptionalEntity.of(credential.getUser()); }); } + + public void setAcquisitionTimeout(long acquisitionTimeout) { + this.acquisitionTimeout = acquisitionTimeout; + } }