fix #2058 add roles/groups

This commit is contained in:
Shinsuke Sugaya 2019-03-20 23:14:15 +09:00
parent b4029ba2ef
commit ccdd233d9e
2 changed files with 108 additions and 35 deletions

View file

@ -15,13 +15,11 @@
*/
package org.codelibs.fess.app.web.base.login;
import static org.codelibs.core.stream.StreamUtil.split;
import static org.codelibs.core.stream.StreamUtil.stream;
import java.util.HashSet;
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;
@ -49,25 +47,7 @@ public class AzureAdCredential implements LoginCredential, FessCredential {
}
public AzureAdUser getUser() {
return new AzureAdUser(authResult, getDefaultGroupsAsArray(), getDefaultRolesAsArray());
}
protected static String[] getDefaultGroupsAsArray() {
final String value = ComponentUtil.getFessConfig().getSystemProperty("azuread.default.groups");
if (StringUtil.isBlank(value)) {
return StringUtil.EMPTY_STRINGS;
} else {
return split(value, ",").get(stream -> stream.filter(StringUtil::isNotBlank).map(s -> s.trim()).toArray(n -> new String[n]));
}
}
protected static String[] getDefaultRolesAsArray() {
final String value = ComponentUtil.getFessConfig().getSystemProperty("azuread.default.roles");
if (StringUtil.isBlank(value)) {
return StringUtil.EMPTY_STRINGS;
} else {
return split(value, ",").get(stream -> stream.filter(StringUtil::isNotBlank).map(s -> s.trim()).toArray(n -> new String[n]));
}
return new AzureAdUser(authResult);
}
public static class AzureAdUser implements FessUser {
@ -81,10 +61,10 @@ public class AzureAdCredential implements LoginCredential, FessCredential {
protected AuthenticationResult authResult;
public AzureAdUser(final AuthenticationResult authResult, final String[] groups, final String[] roles) {
public AzureAdUser(final AuthenticationResult authResult) {
this.authResult = authResult;
this.groups = groups;
this.roles = roles;
final AzureAdAuthenticator authenticator = ComponentUtil.getComponent(AzureAdAuthenticator.class);
authenticator.updateMemberOf(this);
}
@Override
@ -123,7 +103,21 @@ public class AzureAdCredential implements LoginCredential, FessCredential {
final AzureAdAuthenticator authenticator = ComponentUtil.getComponent(AzureAdAuthenticator.class);
final String refreshToken = authResult.getRefreshToken();
authResult = authenticator.getAccessToken(refreshToken);
return false;
authenticator.updateMemberOf(this);
permissions = null;
return true;
}
public AuthenticationResult getAuthenticationResult() {
return authResult;
}
public void setGroups(final String[] groups) {
this.groups = groups;
}
public void setRoles(final String[] roles) {
this.roles = roles;
}
}
}

View file

@ -15,9 +15,16 @@
*/
package org.codelibs.fess.sso.aad;
import static org.codelibs.core.stream.StreamUtil.split;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -32,8 +39,12 @@ import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.core.net.UuidUtil;
import org.codelibs.curl.Curl;
import org.codelibs.curl.CurlResponse;
import org.codelibs.elasticsearch.runner.net.EcrCurl;
import org.codelibs.fess.app.web.base.login.ActionResponseCredential;
import org.codelibs.fess.app.web.base.login.AzureAdCredential;
import org.codelibs.fess.app.web.base.login.AzureAdCredential.AzureAdUser;
import org.codelibs.fess.app.web.base.login.FessLoginAssist.LoginCredentialResolver;
import org.codelibs.fess.crawler.Constants;
import org.codelibs.fess.exception.SsoLoginException;
@ -61,15 +72,15 @@ public class AzureAdAuthenticator implements SsoAuthenticator {
private static final Logger logger = LoggerFactory.getLogger(AzureAdAuthenticator.class);
protected static final String AZUREAD_STATE_TTL = "azuread.state.ttl";
protected static final String AZUREAD_STATE_TTL = "aad.state.ttl";
protected static final String AZUREAD_AUTHORITY = "azuread.authority";
protected static final String AZUREAD_AUTHORITY = "aad.authority";
protected static final String AZUREAD_TENANT = "azuread.tenant";
protected static final String AZUREAD_TENANT = "aad.tenant";
protected static final String AZUREAD_CLIENT_SECRET = "azuread.client.secret";
protected static final String AZUREAD_CLIENT_SECRET = "aad.client.secret";
protected static final String AZUREAD_CLIENT_ID = "azuread.client.id";
protected static final String AZUREAD_CLIENT_ID = "aad.client.id";
protected static final String STATES = "aadStates";
@ -215,7 +226,7 @@ public class AzureAdAuthenticator implements SsoAuthenticator {
}
}
public AuthenticationResult getAccessToken(String refreshToken) {
public AuthenticationResult getAccessToken(final String refreshToken) {
final String authority = getAuthority() + getTenant() + "/";
if (logger.isDebugEnabled()) {
logger.debug("refreshToken: {}, authority: {}", refreshToken, authority);
@ -223,15 +234,15 @@ public class AzureAdAuthenticator implements SsoAuthenticator {
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
AuthenticationContext context = new AuthenticationContext(authority, true, service);
Future<AuthenticationResult> future =
final AuthenticationContext context = new AuthenticationContext(authority, true, service);
final 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) {
} catch (final Exception e) {
throw new SsoLoginException("Failed to get a token.", e);
} finally {
if (service != null) {
@ -315,6 +326,74 @@ public class AzureAdAuthenticator implements SsoAuthenticator {
return params.containsKey(ERROR) || params.containsKey(ID_TOKEN) || params.containsKey(CODE);
}
public void updateMemberOf(final AzureAdUser user) {
final List<String> groupList = new ArrayList<>();
final List<String> roleList = new ArrayList<>();
groupList.addAll(getDefaultGroupList());
roleList.addAll(getDefaultRoleList());
try (CurlResponse response =
Curl.get("https://graph.microsoft.com/v1.0/me/memberOf")
.header("Authorization", "Bearer " + user.getAuthenticationResult().getAccessToken())
.header("Accept", "application/json").execute()) {
final Map<String, Object> contentMap = response.getContent(EcrCurl.jsonParser);
if (contentMap.containsKey("value")) {
@SuppressWarnings("unchecked")
final List<Map<String, Object>> memberOfList = (List<Map<String, Object>>) contentMap.get("value");
for (final Map<String, Object> memberOf : memberOfList) {
if (logger.isDebugEnabled()) {
logger.debug("member: {}", memberOf);
}
final String id = (String) memberOf.get("id");
if (StringUtil.isBlank(id)) {
logger.warn("id is empty: {}", memberOf);
continue;
}
String memberType = (String) memberOf.get("@odata.type");
if (memberType == null) {
logger.warn("@odata.type is null: {}", memberOf);
continue;
}
memberType = memberType.toLowerCase(Locale.ENGLISH);
if (memberType.contains("group")) {
groupList.add(id);
} else if (memberType.contains("role")) {
roleList.add(id);
} else {
if (logger.isDebugEnabled()) {
logger.debug("unknown @odata.type: {}", memberOf);
}
groupList.add(id);
}
}
} else if (contentMap.containsKey("error")) {
logger.warn("Failed to access groups/roles: {}", contentMap);
}
} catch (final IOException e) {
logger.warn("Failed to access groups/roles in AzureAD.", e);
}
user.setGroups(groupList.toArray(n -> new String[n]));
user.setRoles(roleList.toArray(n -> new String[n]));
}
protected List<String> getDefaultGroupList() {
final String value = ComponentUtil.getFessConfig().getSystemProperty("aad.default.groups");
if (StringUtil.isBlank(value)) {
return Collections.emptyList();
} else {
return split(value, ",").get(stream -> stream.filter(StringUtil::isNotBlank).map(s -> s.trim()).collect(Collectors.toList()));
}
}
protected List<String> getDefaultRoleList() {
final String value = ComponentUtil.getFessConfig().getSystemProperty("aad.default.roles");
if (StringUtil.isBlank(value)) {
return Collections.emptyList();
} else {
return split(value, ",").get(stream -> stream.filter(StringUtil::isNotBlank).map(s -> s.trim()).collect(Collectors.toList()));
}
}
protected class StateData {
private final String nonce;
private final long expiration;
@ -365,7 +444,7 @@ public class AzureAdAuthenticator implements SsoAuthenticator {
});
}
public void setAcquisitionTimeout(long acquisitionTimeout) {
public void setAcquisitionTimeout(final long acquisitionTimeout) {
this.acquisitionTimeout = acquisitionTimeout;
}
}